Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 76 additions & 1 deletion docs/docs/fundamentals/core-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,79 @@ sidebar_position: 3

# Core concepts

<!-- TODO: write content for this page -->
You've built an editor. Before going on, it's worth understanding the few
ideas the whole library is built on. They explain why the API looks the way it
does and help you understand further chapters better.

## The input is uncontrolled

`EnrichedTextInput` does not take its content from a prop and does not push
every keystroke back into React state. It owns its content on the native side
and you talk to it through a `ref`.

This is deliberate. Rich text changes constantly — every character, selection
move and style toggle — and round-tripping all of that through JavaScript state
would be extremely slow and open to a possible de-synchronization of those states.
Keeping it native makes the editor fast and stable.

In practice this means:

- To **change** the content or formatting, call a method on the ref, e.g. `ref.current?.toggleBold()`, `ref.current?.setValue(html)`, or `ref.current?.setLink(...)`.
- To **observe** changes to the content or formatting, listen to events such as `onChangeState`, `onChangeHtml`, or `onChangeSelection`.

You never set a `value` prop and re-render to make an edit happen.

## HTML is the source of truth

The editor's content is HTML. `setValue` and `defaultValue` seeds it with an HTML string,
`getHTML` reads the current content back, and `onChangeHtml` streams it as it
changes. What you store and what you render is a string of HTML.

The library uses a fixed set of standard and custom tags, so the output is
predictable and portable. [Supported tags](/fundamentals/html-format-and-supported-tags)
lists exactly what it produces and accepts.

:::caution

Sanitizing HTML is your responsibility. The library doesn't guarantee safe HTML, so
sanitize anything you persist, render elsewhere, or accept from untrusted
sources.

:::

## Normalization

HTML can be messy. Whether users paste rich text from applications like Google Docs or Microsoft Word, or you inject external HTML via `defaultValue`, `setValue`, or pass it directly as `children` into `EnrichedText`, the markup often contains additional wrapper elements, inline styles, and structural quirks that may not match the HTML structure expected by the library..
Comment thread
hejsztynx marked this conversation as resolved.
Outdated

To handle this, both components provide a `useHtmlNormalizer` prop that normalizes incoming HTML. The normalizer cleans and restructures the input into the predictable format the library relies on (e.g., it maps `<strong>` to `<b>`, unwraps `<div>` containers into `<p>` tags, and strips unsupported tags). The `useHtmlNormalizer` prop defaults to `true`.

All supported and canonical tags are listed in [Supported tags](/fundamentals/html-format-and-supported-tags).

## Two components, one HTML format

The library is split into an editor and a viewer:

- **`EnrichedTextInput`** — the interactive editor from the previous page.
- **`EnrichedText`** — a read-only display component that renders the input's
HTML.

The HTML format that both components expect is identical, what allows you to integrate them seamlessly. A common setup edits in `EnrichedTextInput`, stores the `getHTML` output, and later displays it with `EnrichedText`.

## The style state model

Not every style can be combined with every other. For example, a paragraph can't be both a heading and a list item, code blocks don't support inline formatting such as bold or italic. The editor tracks this and reports it through `onChangeState`, which gives each style three booleans:

- **`isActive`** — the style is applied at the current selection. Use it to
highlight a toolbar button.
- **`isBlocking`** — another active style forbids this one entirely, so toggling
it would do nothing. For example bold is blocked inside a code block. Use it
to disable a button.
- **`isConflicting`** — this style would replace an active one if toggled on.
For example switching a blockquote paragraph to a heading removes the
blockquote. Use it to hint that the toggle is a swap, not an addition.

Driving your toolbar from these three flags keeps the UI honest: buttons light
up and grey out according to the editor's state.

For the comprehensive list of which styles block or conflict with each other, see
[Supported tags](/fundamentals/html-format-and-supported-tags).
126 changes: 125 additions & 1 deletion docs/docs/fundamentals/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,130 @@ slug: /
sidebar_position: 1
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Getting started

<!-- TODO: write content for this page -->
The goal of the _Fundamentals_ section is to get you from an empty project to a
working rich text editor and to give you the mental model you need to build on
your own. Let's dive in.

## What is React Native Enriched HTML?

React Native Enriched HTML is a rich text solution for React Native built by
[Software Mansion](https://swmansion.com/). It supports iOS, Android and Web.

It ships two fully native components: `EnrichedTextInput`, a rich text editor
that styles text live as you type, and `EnrichedText`, a read-only display component that
renders the input's output. Both speak HTML — the input
produces it, the display consumes it.

Not only does this library allow you to apply basic rich text styles you know well,
like *bold* or _italic_, but also provides more powerful tools like `mentions`, something
you will learn more about in the next chapters.

## Prerequisites

The library works only with the
[React Native New Architecture (Fabric)](https://reactnative.dev/architecture/landing-page).
It's enabled by default on recent React Native versions; if your app still runs
the old architecture you'll need to switch before installing.

For the exact React Native versions we support, see
[Compatibility](/misc/compatibility).

## Installation

### Bare React Native

Install the package:

<Tabs groupId="package-managers">
<TabItem value="npm" label="NPM">
```bash
npm install react-native-enriched-html
```
</TabItem>

<TabItem value="yarn" label="YARN">
```bash
yarn add react-native-enriched-html
```
</TabItem>
</Tabs>

The library contains native code, so rebuild your app after installing. On iOS,
install the pods first:

```sh
cd ios && bundler install && bundler exec pod install
```

### Expo

Install with the Expo CLI so the correct version is picked for your SDK:

```sh
npx expo install react-native-enriched-html
```

Then regenerate the native projects:

```sh
npx expo prebuild
```

:::note

The library needs native code, so it won't run in Expo Go. Use a
[development build](https://docs.expo.dev/develop/development-builds/introduction/)
instead.

:::

### Web

Install the package:

<Tabs groupId="package-managers">
<TabItem value="npm" label="NPM">
```bash
npm install react-native-enriched-html
```
</TabItem>

<TabItem value="yarn" label="YARN">
```bash
yarn add react-native-enriched-html
```
</TabItem>
</Tabs>

The shipped components share the React Native's API, minus the functionalities a
browser can't provide. See the _Web support_ guide for what's covered.

## Nightly builds

To try features before they land in a stable release, install the nightly
build:

<Tabs groupId="package-managers">
<TabItem value="npm" label="NPM">
```bash
npm install react-native-enriched-html@nightly
```
</TabItem>

<TabItem value="yarn" label="YARN">
```bash
yarn add react-native-enriched-html@nightly
```
</TabItem>
</Tabs>

Nightlies are published to npm automatically and may contain breaking changes.

---

You're set up. Next, let's [build your first editor](/fundamentals/your-first-editor).
76 changes: 75 additions & 1 deletion docs/docs/fundamentals/html-format-and-supported-tags.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,78 @@ sidebar_position: 4

# HTML format and supported tags

<!-- TODO: write content for this page -->
The editor works with a fixed set of standard and custom HTML tags — it both
produces them in its output and accepts them as input. This page is the
reference for that set.

Styles fall into two groups: **inline** tags that wrap a range of characters,
and **paragraph** tags that apply to whole lines. Not all of them combine
freely, and there are two kinds of restriction:

- **Conflicting** — toggling a style that conflicts with an active one replaces
it. Toggling `<h2>` on a `<blockquote>` paragraph removes the blockquote and
applies the heading.
- **Blocking** — a blocked style can't be toggled at all while the blocking
style is active. `<b>` is blocked inside `<codeblock>`, so bold can't be
applied there.

Both show up in the [`onChangeState`](/fundamentals/core-concepts#the-style-state-model) payload as
`isConflicting` and `isBlocking`.

## Inline tags

| Style | HTML tag | Conflicts with | Blocked by |
| ------------- | ----------- | ---------------------------- | ---------------------- |
| Bold | `<b>` | -- | `<codeblock>` |
| Italic | `<i>` | -- | `<codeblock>` |
| Underline | `<u>` | -- | `<codeblock>` |
| Strikethrough | `<s>` | -- | `<codeblock>` |
| Inline code | `<code>` | `<a>`, `<mention>` | `<codeblock>`, `<img>` |
| Link | `<a>` | `<code>`, `<a>`, `<mention>` | `<codeblock>`, `<img>` |
| Mention | `<mention>` | `<code>`, `<a>` | `<codeblock>`, `<img>` |
| Image | `<img>` | `<a>`, `<mention>` | `<code>` |

:::note

Headings also block bold when `bold: true` is set on the heading in the
`htmlStyle` prop. The heading already renders as bold, so toggling bold on top
of it is redundant and therefore blocked.

:::

## Paragraph tags

Only one paragraph-level style can be active per paragraph — they all conflict
with each other.

Some paragraph styles are containers that wrap each line inside them with an
**inner content tag**. Each line of a `<ul>` is wrapped in `<li>`, and each line
of a `<codeblock>` is wrapped in `<p>`.

| Style | HTML tag | Inner content tag | Conflicts with | Blocked by |
| -------------- | --------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| Heading 1 | `<h1>` | -- | `<h2>`, `<h3>`, `<h4>`, `<h5>`, `<h6>`, `<ul>`, `<ol>`, `<ul data-type="checkbox">`, `<blockquote>`, `<codeblock>` | -- |
| Heading 2 | `<h2>` | -- | `<h1>`, `<h3>`, `<h4>`, `<h5>`, `<h6>`, `<ul>`, `<ol>`, `<ul data-type="checkbox">`, `<blockquote>`, `<codeblock>` | -- |
| Heading 3 | `<h3>` | -- | `<h1>`, `<h2>`, `<h4>`, `<h5>`, `<h6>`, `<ul>`, `<ol>`, `<ul data-type="checkbox">`, `<blockquote>`, `<codeblock>` | -- |
| Heading 4 | `<h4>` | -- | `<h1>`, `<h2>`, `<h3>`, `<h5>`, `<h6>`, `<ul>`, `<ol>`, `<ul data-type="checkbox">`, `<blockquote>`, `<codeblock>` | -- |
| Heading 5 | `<h5>` | -- | `<h1>`, `<h2>`, `<h3>`, `<h4>`, `<h6>`, `<ul>`, `<ol>`, `<ul data-type="checkbox">`, `<blockquote>`, `<codeblock>` | -- |
| Heading 6 | `<h6>` | -- | `<h1>`, `<h2>`, `<h3>`, `<h4>`, `<h5>`, `<ul>`, `<ol>`, `<ul data-type="checkbox">`, `<blockquote>`, `<codeblock>` | -- |
| Unordered list | `<ul>` | `<li>` | `<h1>`, `<h2>`, `<h3>`, `<h4>`, `<h5>`, `<h6>`, `<ol>`, `<ul data-type="checkbox">`, `<blockquote>`, `<codeblock>` | -- |
| Ordered list | `<ol>` | `<li>` | `<h1>`, `<h2>`, `<h3>`, `<h4>`, `<h5>`, `<h6>`, `<ul>`, `<ul data-type="checkbox">`, `<blockquote>`, `<codeblock>` | -- |
| Checkbox list | `<ul data-type="checkbox">` | `<li>` / `<li checked>` | `<h1>`, `<h2>`, `<h3>`, `<h4>`, `<h5>`, `<h6>`, `<ul>`, `<ol>`, `<blockquote>`, `<codeblock>` | -- |
| Blockquote | `<blockquote>` | `<p>` | `<h1>`, `<h2>`, `<h3>`, `<h4>`, `<h5>`, `<h6>`, `<ul>`, `<ol>`, `<ul data-type="checkbox">`, `<codeblock>` | -- |
| Codeblock | `<codeblock>` | `<p>` | `<h1>`, `<h2>`, `<h3>`, `<h4>`, `<h5>`, `<h6>`, `<b>`, `<u>`, `<i>`, `<s>`, `<ul>`, `<ol>`, `<ul data-type="checkbox">`, `<blockquote>`, `<code>`, `<mention>`, `<a>` | -- |

## Plain and empty paragraphs

A plain line of text with no paragraph style is wrapped in `<p>`. An empty line
is represented by `<br>`. So a document with a heading, a blank line and a
paragraph comes out like this:

```html
<html>
<h1>Title</h1>
<br>
<p>Some body text.</p>
</html>
```
7 changes: 0 additions & 7 deletions docs/docs/fundamentals/your-first-editor.md

This file was deleted.

Loading
Loading