# Draftail and Draft.js documentation
> Complete documentation for Draftail, a configurable rich text editor built with Draft.js. Also includes the Draft.js documentation directly combined into the Draftail documentation.
This file contains all documentation content in a single document following the llmstxt.org standard.
## Getting started
Draftail is built with [Draft.js](https://draftjs.org/) and [React](https://reactjs.org/). Let’s start by installing them both, as well as Draftail:
```sh
npm install --save draftail draft-js@0.10.5 react react-dom
```
We will also need to import the styles of Draft.js, and of the editor. In a Sass stylesheet:
```scss
@import "draft-js/dist/Draft";
@import "draftail/dist/draftail";
```
Or from a Webpack / Create React App setup, in a JS file:
```js
```
Then, import the editor and use it in your code as a React component. Here is a simple example:
```jsx
const initial = JSON.parse(sessionStorage.getItem("draftail:content"))
const onSave = (content) => {
console.log("saving", content)
sessionStorage.setItem("draftail:content", JSON.stringify(content))
}
const editor = (
)
ReactDOM.render(editor, document.querySelector("[data-mount]"))
```
In this example, the editor will have four buttons in its toolbar: H3, bullet list, bold, and italic. Here is a demo of the result:
Draftail supports many more [formatting options](../introduction/formatting-options.md). Be sure to also check out the [required polyfills](../reference/browser-support.md).
## Controlled component
Optionally, the editor can also be used as a [controlled component](https://reactjs.org/docs/forms.html#controlled-components) like a standard Draft.js editor, with its editor state managed externally via the [`editorState` and `onChange`](../reference/api.md#editorstate-and-onchange) props. If you’re interested in this, have a look at the [controlled component](../reference/controlled-component.md) section of the documentation.
## Why we need Draft.js and React
[Draft.js](https://draftjs.org/) is the framework that Draftail is built upon, meant for rich text experiences in React-driven UIs. **Draftail is an opinionated implementation of a Draft.js editor** – abstracting away the complexities for the simple use cases.
You don’t need any Draft.js knowledge to make use of Draftail, unless you want to invest into more custom rich text formatting. React knowledge is likely needed, however.
If you want to learn more about Draftail’s implementation, read on: [Why Wagtail’s new editor is built with Draft.js](/blog/2018/03/05/why-wagtail-new-editor-is-built-with-draft-js).
### Usage without React
While Draftail depends on React, it’s perfectly possible to use it in a project that otherwise doesn’t use React. There are however a lot of other rich text editors that might be better suited for such scenarios – and writing Draftail extensions for any non-trivial formatting will still require writing React code
---
## API reference
For projects using TypeScript, Draftail includes type definitions.
## DraftailEditor
```js
const editor =
```
To change the behavior of the editor, pass props to the `DraftailEditor` component. Here are the available props, and their default values:
```jsx
/** Initial content of the editor. Use this to edit pre-existing content. */
rawContentState?: RawDraftContentState | null
/** Called when changes occurred. Use this to persist editor content. */
onSave?: ((content: null | RawDraftContentState) => void) | null
/** Content of the editor, when using the editor as a controlled component. Incompatible with `rawContentState` and `onSave`. */
editorState?: EditorState | null
/** Called whenever the editor state is updated. Use this to manage the content of a controlled editor. Incompatible with `rawContentState` and `onSave`. */
onChange?: ((editorState: EditorState) => void) | null
/** Called when the editor receives focus. */
onFocus?: (() => void) | null
/** Called when the editor loses focus. */
onBlur?: (() => void) | null
/** Displayed when the editor is empty. Hidden if the user changes styling. */
placeholder?: string | null
/** Enable the use of horizontal rules in the editor. */
enableHorizontalRule: BoolControl
/** Enable the use of line breaks in the editor. */
enableLineBreak: BoolControl
/** Show undo control in the toolbar. */
showUndoControl: BoolControl
/** Show redo control in the toolbar. */
showRedoControl: BoolControl
/** Disable copy/paste of rich text in the editor. Default: true */
stripPastedStyles: boolean
/** Set if the editor supports multiple lines / blocks of text, or only a single line. Default: true */
multiline: boolean
/** Set whether spellcheck is turned on for your editor.
* See https://draftjs.org/docs/api-reference-editor.html#spellcheck.
*/
spellCheck: boolean
/** Set whether the editor should be rendered in readOnly mode.
* See https://draftjs.org/docs/api-reference-editor.html#readonly
*/
readOnly: boolean
/** Optionally set the overriding text alignment for this editor.
* See https://draftjs.org/docs/api-reference-editor.html#textalignment.
*/
textAlignment?: string | null
/** Optionally set the overriding text directionality for this editor.
* See https://draftjs.org/docs/api-reference-editor.html#textdirectionality.
*/
textDirectionality: TextDirectionality
/** Set if auto capitalization is turned on and how it behaves.
* See https://draftjs.org/docs/api-reference-editor.html#autocapitalize-string.
*/
autoCapitalize?: string | null
/** Set if auto complete is turned on and how it behaves.
* See https://draftjs.org/docs/api-reference-editor.html#autocomplete-string.
*/
autoComplete?: string | null
/** Set if auto correct is turned on and how it behaves.
* See https://draftjs.org/docs/api-reference-editor.html#autocorrect-string.
*/
autoCorrect?: string | null
/** See https://draftjs.org/docs/api-reference-editor.html#aria-props. */
ariaDescribedBy?: string | null
ariaExpanded?: boolean | null
ariaLabel?: string | null
ariaLabelledBy?: string | null
ariaOwneeID?: string | null
ariaRequired?: string | null
/** List of the available block types. */
blockTypes: ReadonlyArray
/** List of the available inline styles. */
inlineStyles: ReadonlyArray
/** List of the available entity types. */
entityTypes: ReadonlyArray
/** List of active decorators. */
decorators: ReadonlyArray
/** List of extra toolbar controls. */
controls: ReadonlyArray
/** Optionally enable the command palette UI. */
commands: boolean | ReadonlyArray
/** List of plugins of the draft-js-plugins architecture. */
plugins: ReadonlyArray
/** Optionally override the default Draftail toolbar, removing or replacing it. Default: Toolbar */
topToolbar?: React.ComponentType | null
/** Optionally add a custom toolbar underneath the editor, e.g. for metrics. */
bottomToolbar?: React.ComponentType | null
/** Optionally override the default command toolbar, removing or replacing it. Default: CommandPalette */
commandToolbar?: React.ComponentType | null
/** Max level of nesting for list items. 0 = no nesting. Maximum = 10. Default: 1 */
maxListNesting: number
/** Frequency at which to call the onSave callback (ms). Default: 250 */
stateSaveInterval: number
```
### rawContentState and onSave
`rawContentState` and `onSave` are used to initialise the editor with content, and to periodically save new content. They work with [raw ContentState](../reference/content-storage.md) objects representing the editor’s content, or `null` if the editor is empty.
This is the editor’s [uncontrolled component](https://reactjs.org/docs/uncontrolled-components.html) API, which is easier to use for simple implementations. Have a look at the [controlled component](../reference/controlled-component.md) API as well, with [`editorState` and `onChange`](#editorstate-and-onchange).
### editorState and onChange
`editorState` and `onChange` are used to set the state of the editor, and update this state whenever there are changes to the editor’s content or selection. They work with [`EditorState`](../reference/content-storage.md#editorstate-vs-contentstate) objects representing all of the editor’s state.
This is the editor’s [controlled component](https://reactjs.org/docs/forms.html#controlled-components) API, matching that of other Draft.js examples.
### Inline styles
How-to guide: [Inline styles](../introduction/inline-styles.md)
```jsx
const editor =
```
Each item in `inlineStyles` can have the following props:
```jsx
// Unique type shared between inline style instances.
type: string,
// CSS properties (in JS format) to apply for styling within the editor area.
style?: CSSProperties;
// Describes the control in the editor UI, concisely.
label?: string | null;
// Describes the control in the editor UI.
description?: string | null;
// Represents the control in the editor UI.
icon?: IconProp;
```
### Blocks
How-to guide: [Blocks](../introduction/blocks.md)
```jsx
const editor =
```
Each item in `blockTypes` can have the following props:
```jsx
// Unique type shared between block instances.
type: string,
// Describes the control in the editor UI, concisely.
label?: string | null;
// Describes the control in the editor UI.
description?: string | null;
// Represents the control in the editor UI.
icon?: IconProp;
// DOM element used to display the block within the editor area.
element?: string;
```
### Entities
How-to guide: [Entities](../introduction/entities.md)
```jsx
const editor =
```
Each item in `entityTypes` can have the following props:
```jsx
// Unique type shared between entity instances.
type: string,
// Describes the control in the editor UI, concisely.
label?: string | null;
// Describes the control in the editor UI.
description?: string | null;
// Represents the control in the editor UI.
icon?: IconProp;
/** React component providing the UI to manage entities of this type. */
source: React.ComponentType;
/** React component to display inline entities. */
decorator?: React.ComponentType;
/** React component to display block-level entities. */
block?: React.ComponentType;
/** Custom copy-paste processing checker. */
onPaste?: (
text: string,
html: string | null | undefined,
editorState: EditorState,
helpers: {
setEditorState: (state: EditorState) => void;
getEditorState: () => EditorState;
},
entityType: EntityTypeControl,
) => "handled" | "not-handled";
/** Array of attributes the entity uses, to preserve when filtering entities on paste.
* If undefined, all entity data is preserved.
*/
attributes?: ReadonlyArray;
/** Attribute - regex mapping, to preserve entities based on their data on paste.
* For example, { url: '^https:' } will only preserve links that point to HTTPS URLs.
*/
allowlist?: { [attr: string]: string };
```
### Decorators
How-to guide: [Decorators](../introduction/decorators.md)
```jsx
const editor =
```
Each item in `decorators` can have the following props:
```jsx
// Determines which pieces of content are to be decorated.
strategy: (block: ContentBlock, callback: (start: number, end: number) => void, contentState: ContentState) => void,
// React component to display the decoration.
component: ComponentType<{}>,
```
### Controls
How-to guide: [Controls](../introduction/arbitrary-controls.md)
```jsx
const editor =
```
Each item in `controls` can either be `inline`, `block`, or `meta`, and have the following props:
```jsx
// Or block or meta.
inline: {
// Retrieve the full Draft.js EditorState.
getEditorState: () => EditorState,
// Change any part of the EditorState.
onChange: (EditorState) => void,
}
```
### Plugins
How-to guide: [Plugins](../introduction/plugins.md)
```jsx
const editor =
```
Each item in `plugins` follows the [draft-js-plugins API](https://github.com/draft-js-plugins/draft-js-plugins/blob/master/HOW_TO_CREATE_A_PLUGIN.md).
## Managing focus
The `DraftailEditor` has a `focus()` API [like that of Draft.js](https://draftjs.org/docs/advanced-topics-managing-focus.html#content). Use it to imperatively move focus to the editor. There are also `onFocus` and `onBlur` props to hook into the editor’s focus lifecycle, for example for [form validation](/guides/form-validation.md).
## Content format identifiers
Draftail exports identifiers for common rich text formats to ensure the same identifiers are used consistently.
```js
```
For inline styles:
```js
// See https://github.com/facebook/draft-js/blob/master/src/model/immutable/DefaultDraftInlineStyle.js
export const INLINE_STYLE = {
BOLD: "BOLD",
ITALIC: "ITALIC",
CODE: "CODE",
UNDERLINE: "UNDERLINE",
STRIKETHROUGH: "STRIKETHROUGH",
MARK: "MARK",
QUOTATION: "QUOTATION",
SMALL: "SMALL",
SAMPLE: "SAMPLE",
INSERT: "INSERT",
DELETE: "DELETE",
KEYBOARD: "KEYBOARD",
SUPERSCRIPT: "SUPERSCRIPT",
SUBSCRIPT: "SUBSCRIPT",
}
```
For blocks:
```js
// See https://github.com/facebook/draft-js/blob/master/src/model/immutable/DefaultDraftBlockRenderMap.js
export const BLOCK_TYPE = {
// This is used to represent a normal text block (paragraph).
UNSTYLED: "unstyled",
HEADER_ONE: "header-one",
HEADER_TWO: "header-two",
HEADER_THREE: "header-three",
HEADER_FOUR: "header-four",
HEADER_FIVE: "header-five",
HEADER_SIX: "header-six",
UNORDERED_LIST_ITEM: "unordered-list-item",
ORDERED_LIST_ITEM: "ordered-list-item",
BLOCKQUOTE: "blockquote",
CODE: "code-block",
// This represents a "custom" block, not for rich text, with arbitrary content.
ATOMIC: "atomic",
}
```
For entities:
```js
export const ENTITY_TYPE = {
LINK: "LINK",
IMAGE: "IMAGE",
HORIZONTAL_RULE: "HORIZONTAL_RULE",
}
```
## Data conversion helpers
How-to guide: [Data conversion helpers](../reference/controlled-component.md#data-conversion-helpers)
Draftail exports the methods it uses internally to initialise the editor’s content via `rawContentState` and persist it in `onSave`: [`createEditorStateFromRaw`](#createeditorstatefromraw), and [`serialiseEditorStateToRaw`](#serialiseeditorstatetoraw).
### createEditorStateFromRaw
Creates a new EditorState from a RawDraftContentState, or an empty editor state by passing `null`.
```js
createEditorStateFromRaw = (rawContentState: ?RawDraftContentState) =>
EditorState
```
### serialiseEditorStateToRaw
Serialises the editorState using `convertToRaw`, but returns `null` if the editor content is empty (no text, entities, styles).
```js
serialiseEditorStateToRaw = (editorState: EditorState) => RawDraftContentState
```
## Reusable UI components
Some of Draftail’s UI components can be reused to more easily build extensions that are consistent.
### Icon
The Icon can be reused to have consistent icon sizing between different extensions and the toolbar.
```js
const icon =
```
Supported props:
```jsx
icon?: string | string[] | JSX.Element;;
title?: string | null;
className?: string | null;
```
There is further documentation about what formats are allowed for `icon`: [Customising icons](../reference/customising-icons.md).
### ToolbarButton
The ToolbarButton can be reused when building custom [`controls`](../introduction/arbitrary-controls.md) in the toolbar.
```js
const button =
```
Supproted props:
```jsx
name: ?string,
active: boolean,
label: ?string,
title: ?string,
icon: ?IconProp,
onClick: ?(string) => void,
```
---
## Arbitrary controls
> Those extensions require a good understanding of the [Draft.js](https://draftjs.org/) API.
Draftail also has an API to add arbitrary controls in the toolbar, via the [`controls`](../reference/api.md#controls-docs-arbitrary-controls) prop. This prop takes an array of objects, each which can have a `inline`, `block`, or `meta` key. This key maps to a React component which will be given a `getEditorState` function and the `onChange` handler as props.
- Use the `inline` key for controls intended for the floating toolbar.
- Use the `block` key for controls intended for the "block" static toolbar at the top of the editor.
- Use the `meta` key for controls intended for the bottom / meta toolbar.
Controls can also have a `type` to help with troubleshooting.
Controls can use multiple keys if they need to be displayed in multiple toolbars.
For the React component props:
- `getEditorState` can be used to retrieve and read the full Draft.js [EditorState](https://draftjs.org/docs/api-reference-editor-state).
- `onChange` can be called with a new EditorState.
The controls can import the `Icon` and `ToolbarButton` components from Draftail if necessary.
## Examples
Controls can be used for a wide range of use cases:
- Generating metrics based on the whole content of the editor
- Applying one-off transformations to the editor (e.g. inserting content from third-party data sources, clear formatting).
---
## Blocks
Blocks provide structure to the content. They do not overlap – no content can be both a paragraph and a heading.
## Built-in blocks
To use built-in blocks, simply use their predefined type.
```jsx
blockTypes={[
{
type: BLOCK_TYPE.BLOCKQUOTE,
},
]}
```
Built-in blocks come with default labels or icons, styles, as well as an english description and often keyboard shortcuts.
## Custom blocks
Simple blocks are very easy to create. Add a new block type to [`blockTypes`](../reference/api.md#blocks-docs-blocks). Here is an example, creating a "Tiny text" block:
```jsx
blockTypes={[
{
type: 'tiny-text',
label: 'Tiny',
},
]}
```
You may also use CSS to style the block, via the `Draftail-block--tiny-text` class:
```css
.Draftail-block--tiny-text {
font-size: 0.7625rem;
font-style: italic;
}
```
### Examples
With a live editor,
## Custom block rendering
For even more advanced blocks requiring custom React components to render, please refer to the [`plugins`](../introduction/plugins.md) API.
---
## Decorators
> Those extensions require a good understanding of the [Draft.js](https://draftjs.org/) API.
Custom decorators follow the Draft.js [CompositeDecorator](https://draftjs.org/docs/advanced-topics-decorators.html#compositedecorator) API. They can be specified as an array via the [`decorators`](../reference/api.md#decorators-docs-decorators) prop of the editor, with `strategy` and `component` attributes.
A very basic example would be a hashtag decorator:
```jsx
decorators={[
{
strategy: (block, callback) => {
const text = block.getText();
let matches;
while ((matches = /#[\w]+/g.exec(text)) !== null) {
callback(matches.index, matches.index + matches[0].length);
}
},
component: ({ children }) => (
{children}
),
},
]}
```
Other more advanced examples could share state between the strategy and its rendering. For example, to build [syntax highlighting for code blocks](https://github.com/springload/draftail/blob/c22d867a6b57dc45144b9af3202c08873c24258b/examples/components/PrismDecorator.js) in rich text.
## Examples
---
## Entities
Entities annotate content with data to represent rich content beyond text. They can be inline (e.g. a link on a word), or block-based (e.g. an embedded video). They do not overlap – no content can be both a link and an embedded video (though there could be a combined "embedded video with link" entity).
## Built-in entities
Put simply, there are no built-in entities in Draftail. The idea is to give as much control as possible over the UI as possible, thus having very little included by default, and providing an extensive API.
That said, [Draft.js](https://draftjs.org), on which Draftail is built, does sometimes have special behavior for `LINK` and `IMAGE` entities (for example, it detects `a` and `img` tags in rich text when pasting, and converts them to entities). If possible, always try to use those built-in types before introducing new ones.
```jsx
entityTypes={[
{
type: ENTITY_TYPE.LINK,
// [...]
},
{
type: ENTITY_TYPE.IMAGE,
// [...]
},
]}
```
## Custom entities
Creating custom entity types is more involved than custom blocks and inline styles because entities aren't simply on/off: they often need additional data (thus a UI to enter this data), and can be edited.
> ⚠ The entity API is at a much lower level of abstraction than that of blocks and styles, and knowledge of the [Draft.js API](https://draftjs.org/docs/advanced-topics-entities) is expected, as well as of [React](https://reactjs.org/) components and their lifecycle.
Apart from the usual type/label/description/icon options to pass via objects in [`entityTypes`](../reference/api.md#entities-docs-entities), entities need:
- A `source`, a React component that will be rendered to display the UI when creating or editing an entity. This could involve a modal window, API calls, a tooltip, or any other mean of gathering entity data.
- A `decorator`, a React component to display the entity within the editor area for inline entities (eg. links).
- Finally, the `block` is for block-level entities (think: image block, embed) to supply their React component.
Optionally, entities can also take an `attributes` and `allowlist` props. These can be used to determine allowlisting rules when pasting content into the editor, to only keep the entities considered valid. If undefined, all entities are always preserved with all of their data.
```jsx
{
type: ENTITY_TYPE.IMAGE,
icon: '#icon-image',
// Preserve the src and alt attributes and no other.
attributes: ['src', 'alt'],
// Preserve images for which the src starts with "http".
allowlist: {
src: '^http',
},
}
```
### Sources
Sources are responsible for creating and editing entities, and are toggled when requested from the toolbar, or from a decorator or block. Here is a [simple image source](https://github.com/thibaudcolas/draftail-playground/blob/main/src/entities/ImageSource.tsx) which uses `window.prompt` to ask the user for an image's `src`, then creates an entity and its atomic block:
```js
class ImageSource extends Component {
componentDidMount() {
const { editorState, entityType, onComplete } = this.props
const src = window.prompt("Image URL")
if (src) {
const content = editorState.getCurrentContent()
const contentWithEntity = content.createEntity(
entityType.type,
"IMMUTABLE",
{ src },
)
const entityKey = contentWithEntity.getLastCreatedEntityKey()
const nextState = AtomicBlockUtils.insertAtomicBlock(
editorState,
entityKey,
" ",
)
onComplete(nextState)
} else {
onComplete(editorState)
}
}
render() {
return null
}
}
```
The source component is given the following props:
```jsx
/** The editorState is available for arbitrary content manipulation. */
editorState: EditorState;
/** Takes the updated editorState, or null if there are no changes, and focuses the editor. */
onComplete: (nextState: EditorState) => void;
/** Closes the source, without focusing the editor again. */
onClose: () => void;
/** Current entity to edit, if any. */
entityType: EntityTypeControl;
/** Current entityKey to edit, if any. */
entityKey?: string | null;
/** Whole entityType configuration, as provided to the editor. */
entity?: EntityInstance | null;
/** Optionally set the overriding text directionality for this editor. */
textDirectionality: TextDirectionality;
```
### Decorators
Decorators render inline entities based on their data.
```jsx
const Link = ({ entityKey, contentState, children }) => {
const { url } = contentState.getEntity(entityKey).getData()
return (
{children}
)
}
```
They receive the following props:
```jsx
/** The key of the decorated entity. */
entityKey: string;
/** The editor’s content. */
contentState: ContentState;
/** Rich text to be displayed inside the decorator. */
children: React.ReactNode;
/** Shorthand to edit entity data. */
onEdit: (entityKey: string) => void;
/** Shorthand to remove an entity, and the related block. */
onRemove: (entityKey: string, blockKey?: string) => void;
/** Optionally set the overriding text directionality for this editor. */
textDirectionality: TextDirectionality;
```
The `onEdit` and `onRemove` props are meant so decorators can also serve in managing entities, eg. to build tooltips to edit links.
### Blocks
Blocks render block-level entities based on their data, and can contain editing controls. Here is a [simple image block](https://github.com/thibaudcolas/draftail-playground/blob/main/src/entities/ImageBlock.tsx), rendering images with `src` and `alt` attributes:
```jsx
class ImageBlock extends Component {
render() {
const { blockProps } = this.props
const { entity } = blockProps
const { src, alt } = entity.getData()
return
}
}
```
They receive the following props:
```jsx
block: ContentBlock;
blockProps: {
/** The editorState is available for arbitrary content manipulation. */
editorState: EditorState;
/** Current entity to manage. */
entity: EntityInstance;
/** Current entityKey to manage. */
entityKey: string;
/** Whole entityType configuration, as provided to the editor. */
entityType: EntityTypeControl;
/** Make the whole editor read-only, except for the block. */
lockEditor: () => void;
/** Make the editor editable again. */
unlockEditor: () => void;
/** Shorthand to edit entity data. */
onEditEntity: () => void;
/** Shorthand to remove an entity, and the related block. */
onRemoveEntity: () => void;
/** Update the editorState with arbitrary changes. */
onChange: (nextState: EditorState) => void;
/** Optionally set the overriding text directionality for this editor. */
textDirectionality: TextDirectionality;
};
```
### Custom paste processing
Entities can also implement their own cut/copy - paste processing. For example, this can be helpful when pasting specific URLs should be converted to a custom entity rather than a generic link. The `onPaste` API signature for all entity types is:
```jsx
onPaste?: (
text: string,
html: string | null | undefined,
editorState: EditorState,
helpers: {
setEditorState: (state: EditorState) => void;
getEditorState: () => EditorState;
},
entityType: EntityTypeControl,
) => "handled" | "not-handled";
```
When implementing this function, check the `text` and `html` arguments to determine if the pasted content should be converted to an entity. If so, return `"handled"` to prevent the default Draft.js behavior, and call the `setEditorState` function to update the editor state with the desired processing.
### Content storage
Refer to our [Content storage](../reference/content-storage.md#entities) documentation for information on how entities are stored in Draft.js.
### Examples
Here is an example of what this would look like in practice, with a very simple implementation of link and image chooser UIs.
---
## Formatting options
Draftail, like Draft.js, distinguishes between 3 content formats:
- [Inline styles](../introduction/inline-styles.md), providing inline formatting for text. They can overlap: text can be both bold and italic.
- [Blocks](../introduction/blocks.md), that provide structure to the content. They do not overlap – no content can be both a paragraph and a heading.
- [Entities](../introduction/entities.md), annotating content with data to represent rich content beyond text. They can be inline (e.g. a link applied on a word), or block-based (e.g. an embedded video). They do not overlap – no content can be both a link and an embedded video (though there could be a combined "embedded video with link" entity).
## Built-in formats
- Block types: H1, H2, H3, H4, H5, H6, Blockquote, Code, UL, OL, P
- Inline styles: Bold, Italic, Underline, Code, Strikethrough, Mark, Keyboard, Superscript, Subscript
- And HR, BR
View the [all-formats example](/examples#all), or try them out below.
Draftail does not come with built-in controls for things like images and links, so you can build your own exactly as you wish. This is particularly useful when integrating with content sources, like a CMS, an API, or other tools with a fixed schema.
## Configuring available formats
By default, the editor provides the least amount of rich text features. Formats have to be explicitly enabled by the developer, so they have as much control over what rich content is available as possible.
To use a given format, add it to the corresponding list, following the options detailed in the next sections.
```jsx
// List of the available block types.
blockTypes: [],
// List of the available inline styles.
inlineStyles: [],
// List of the available entity types.
entityTypes: [],
```
## Custom formats
Draftail is meant to provide a consistent editing experience regardless of what formats (blocks, inline styles, entities) are available. It should be simple for developers to enable/disable a certain format, or to create new ones.
Here are quick questions to help you determine which formatting to use, depending on the use case:
| In order to… | Use |
| ------------------------------------- | ------------------------------------------------- |
| Format a portion of a line | [Inline styles](../introduction/inline-styles.md) |
| Indicate the structure of the content | [Blocks](../introduction/blocks.md) |
| Enter additional data/metadata | [Entities](../introduction/entities.md) |
Then, your mileage may vary! There is good support for custom block-level and inline formatting. Custom entities or decorators require knowledge of the Draft.js API, which is very low-level.
## Single-line support
In addition to different formatting options, the editor also supports single-line rich text fields with the `multiline={false}` prop. Here’s an example:
In this mode, the editor will not allow line breaks, and any multiline pasted text gets converted to a single line.
---
## Inline styles
Inline styles provide inline formatting for rich text. They can overlap: text can be both bold and italic.
## Built-in styles
All you need to do is to use the predefined type for the block, via an object in [`inlineStyles`](../reference/api.md#inline-styles-docs-inline-styles):
```jsx
inlineStyles={[
{
type: INLINE_STYLE.BOLD,
},
]}
```
All built-in styles come with default labels or icons, styles, as well as an english description and often keyboard shortcuts.
## Custom styles
Apart from a `type`, custom inline styles only require a `style` prop, defining which [CSS properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Properties_Reference) to apply when the format is active.
Here is a basic example:
```jsx
inlineStyles={[
{
label: 'Redacted',
type: 'REDACTED',
style: {
backgroundColor: 'currentcolor',
},
},
]}
```
It is also possible to override the styling of predefined inline styles:
```jsx
inlineStyles={[
{
label: 'Bold',
type: INLINE_STYLE.BOLD,
style: {
fontWeight: 'bold',
textShadow: '1px 1px 1px black',
},
},
]}
```
All other props are optional, but styles need to have either a `label` or `icon` in order to appear in the toolbar.
### Examples
Those examples would render as:
---
## Plugins
> Those extensions require a good understanding of the [Draft.js](https://draftjs.org/) API.
Draftail supports plugins following the [Draft.js Plugins](https://www.draft-js-plugins.com/) architecture. Such plugins are the most advanced and powerful type of extension for Draftail, offering customisation capabilities equal to what would be possible with a custom Draft.js editor. From the rendering of any block, to the text input handling, keyboard shortcuts, copy-paste handling – **all that is customisable in a bespoke Draft.js implementation should be customisable with plugins.**
## Reusing existing plugins
Please follow the [official Draft.js Plugins documentation](https://www.draft-js-plugins.com/) for information on how to reuse existing plugins. From the Draftail perspective, this should be very straightforward; for example:
```jsx
const hashtagPlugin = createHashtagPlugin()
const editor =
```
## Creating new plugins
Please have a look at the official [How to create a plugin](https://github.com/draft-js-plugins/draft-js-plugins/blob/master/HOW_TO_CREATE_A_PLUGIN.md) guide. You can also explore the code of [many pre-existing plugins](https://www.npmjs.com/search?q=draft-js-plugins).
## Example
Here is an example of a custom "action list" plugin, which uses a custom block component:
---
## Draft.js vs Draftail
Do you already have some knowledge of Draft.js, and wonder how Draftail differs? Are you wondering which of the two is appropriate for your project? This reference is for you.
## TL;DR;
Draftail is an opinionated editor built with Draft.js, a framework to build rich text experiences. Draft.js is relatively low-level, so Draftail provides high-level APIs to easily implement simple formatting needs (e.g. bold) in a WISYWIG style, as well as providing access to the low-level APIs for more custom extensions.
## High-level APIs
Draftail provides high-level, config-only APIs to create [inline styles](../introduction/inline-styles.md) and [blocks](../introduction/blocks.md). Using those APIs, Draftail provides:
- A toolbar button, with an active state, an icon or label, and tooltip including description and keyboard shortcut
- A keyboard shortcut, for formats that have built-in support
- A Markdown shortcut, for formats that have a Markdown representation
- Default styles for formatted text
- Support to copy-paste custom formats between editors
- Filtering out of inactive formats on paste
The above is also built-in for:
- Line breaks
- Horizontal rules
- To some extent, links and images
## Entities
Additionally to the above, Draftail provides lower-level APIs for [entities](../introduction/entities.md), both inline and block-level. Most of the above capabilities are also built-in for entities, except for:
- Entity rendering, whether block or inline. The React component needs to be provided.
- Entity creation UI – when clicking the button in the toolbar, or editing an existing entity. This is supported by providing a React component that will render and be able to update the Draft.js content.
### Low-level APIs
Inline styles, blocks, and entities should be enough for most WYSIWYG experiences. For more advanced features, there are further low-level APIs available:
- [Decorators](../introduction/decorators.md), access to the [corresponding Draft.js API](https://draftjs.org/docs/advanced-topics-decorators).
- [Controls](../introduction/arbitrary-controls.md), a very simple API to render a React component in the toolbar that can edit the editor content in any way.
- [Plugins](../introduction/plugins.md), API of the [Draft.js Plugins](https://github.com/draft-js-plugins/draft-js-plugins) plugin architecture.
Additionally, the editor supports overriding its [toolbars](../reference/customising-toolbars.md) for even more advanced changes (e.g. use separate modal toolbars for inline and block-level formatting).
## Behind the scenes
Beyond supporting all of those APIs, most of the value in Draftail over plain Draft.js is:
- Having good support for keyboard shortcuts and Markdown handling out of the box. There would be quite a lot of boilerplate code to write to get this with a vanilla editor.
- Advanced support to allowlist only the formats you want the editor to support, automatically [filtering-out](https://github.com/thibaudcolas/draftjs-filters) paste of other formats the editor doesn’t have enabled.
- Support for copy-paste of custom formatting between editors, which Draft.js doesn’t support [out of the box](https://github.com/thibaudcolas/draftjs-conductor).
---
## Extensions tutorial: linkify
Linkify features a type of interaction that’s easy to implement with the [Draft.js plugins](https://www.draft-js-plugins.com/) API (here, the [`handlePastedText`](https://draftjs.org/docs/api-reference-editor#handlepastedtext) API from Draft.js).
It only requires implementing `handlePastedText` and no other API:
```jsx
const linkifyPlugin = () => ({
handlePastedText(
text: string,
html: ?string,
editorState: EditorState,
{ setEditorState }: { setEditorState: (EditorState) => void },
) {
let nextState = editorState
if (text.match(LINKIFY_REGEX_EXACT)) {
const selection = nextState.getSelection()
if (selection.isCollapsed()) {
nextState = createEntity(
nextState,
"LINK",
{ url: text },
text,
"MUTABLE",
)
} else {
const content = nextState.getCurrentContent()
const contentWithEntity = content.createEntity("LINK", "MUTABLE", {
url: text,
})
const entityKey = contentWithEntity.getLastCreatedEntityKey()
nextState = RichUtils.toggleLink(nextState, selection, entityKey)
}
setEditorState(nextState)
return "handled"
}
return "not-handled"
},
})
```
Linkify is a good feature to have as an extension because it is both very useful to end users, and very opinionated – because of the regular expression used to determine whether pasted content is a URL. No URL detection will be perfect, it is important to use a technique that has appropriate false positives and false negatives for the use case.
In the above example, there are two use cases:
- If the selection is collapsed, insert the text of the URL and create a link on it.
- If the selection is not collapsed, insert the pasted URL as a link onto the selected text.
## Example
Here is a demo:
---
## Extensions tutorial: max length
---
## Form validation
Draftail is easy to integrate with form validation, whether as simple "required" checks or more advanced rich text metrics.
## Integrating with a validation library
Content entered in the editor can be stored for validation with the [`onSave`](../reference/api.md#rawcontentstate-and-onsave) prop, or with [`onChange`](../reference/api.md#editorstate-and-onchange). The editor also supports [`onFocus` and `onBlur`](../reference/api.md#managing-focus) to mark fields as touched/untouched and trigger or stop validation.
## Basic validation of required fields
The editor uses `null` as a content value when it is empty. Here is a what a simple “required field” check would look like:
```js
if (!content) {
error = "Please enter at least one paragraph"
}
```
## Content validation
For more advanced use cases, the [raw ContentState](../reference/content-storage.md) content can be parsed to enforce presence or threshold of specific content formats, or content length.
To calculate plain-text content length,
```js
const contentLength = content.blocks.reduce((sum, b) => sum + b.text.length, 0)
```
We can also enforce metrics such as a minimum number of paragraphs (blocks):
```js
if (blocks.filter((b) => b.text.trim().length > 0).length < 2) {
error = "Please enter at least two paragraphs"
}
```
## Demo
Here is a full demo, using [Formik](https://jaredpalmer.com/formik) as a validation library. It enforces entering at least two paragraphs of content, containing at least one link.
```jsx
{
const errors = {}
if (!values.content) {
errors.content = "Please enter at least two paragraphs"
} else {
const { blocks, entityMap } = values.content
if (Object.keys(entityMap).length === 0) {
errors.content = "Please use at least one link"
}
// Check there are at least two blocks with non-whitespace text
if (blocks.filter((b) => b.text.trim().length > 0).length < 2) {
errors.content = "Please enter at least two paragraphs"
}
}
return errors
}}
>
{({ errors, touched, handleSubmit, setFieldTouched, setFieldValue }) => (
)}
```
---
## Getting started with extensions
Do you want to write extensions for Draftail? This is a good place to start. I’ll first try to discourage you from doing it (rich text is messy and unforgiving), then tell what you need to know, and how to proceed.
## Why you shouldn’t mess with rich text editors
It all comes down to [`contenteditable`](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Editable_content), which is very hard to make work. [Draft.js](https://draftjs.org/) partly saves us from this. If you want to know more, read on [Why Wagtail’s new editor is built with Draft.js](/blog/2018/03/05/why-wagtail-new-editor-is-built-with-draft-js).
The TL;DR; is that there are a lot of issues with specific interactions within `contenteditable`. I made a list of the [issues I know about in Draft.js / Draftail](https://github.com/springload/draftail/issues/138). Here are the high-level problems you will have to make peace with:
- [Support for IMEs (Input Method Editor)](https://en.wikipedia.org/wiki/Input_method). which is how [CJK characters](https://en.wikipedia.org/wiki/CJK_characters) are entered, and how OS-level autocomplete and autocorrect works. Differently in each OS/browser, of course.
- Mobile support. For Draft.js, Android Chrome is particularly problematic – because of its implementation of text input as IME in all languages that does not match with other browsers (including Chrome desktop).
- Copy-pasting. Some things will paste fine, some won't. Thankfully, Draftail is built to never allow unwanted formatting.
- Focus management. Not strictly an issue, but something that actually needs to be taken care of for the rich text experience to feel good.
### Draftail vs StreamField
Within Wagtail, [StreamField](http://docs.wagtail.org/en/stable/topics/streamfield.html) already delivers when it comes to free-form content. Sure, it's not rich text, but it's very usable for block-level content. Here is my rule of thumb for when to use one over the other:
- If it's text, use rich text.
- If it's inline text formatting (bold, strikethrough, etc), use rich text.
- If it's block text formatting (h2, blockquote, ul), you get to choose, I personally prefer Draftail because it's nicer (faster) to use than the StreamField UI.
- If it's a block with more than just text, use StreamField.
There may be an opportunity for Draftail to support blocks better in the future, but in the meantime [StreamField is already getting better](https://www.kickstarter.com/projects/noripyt/wagtails-first-hatch) thanks to the work of NoriPyt’s Bertrand.
## When you should use Draftail
With the presentations out of the way, we can now talk about the areas where Draftail extensions can help.
There are three categories of extensions I can think of:
1. Custom links – things that enhance textual content with added data.
2. Tokens, IMEs, autocompletes – help entering content, with or without extra data.
3. Editor metrics, highlighting, helpers – help without necessarily altering content.
### Custom links
Any kind of custom "link" feature is a good fit for a Draftail extension. This can be:
- Links to specific entities from the domain model of your site. This could be links that have a specific icon next to them to denote their target is special, like branches of an chain store.
- Links that are not supported by the traditional "Link" feature. Anchor links come to mind.
- Enhanced links with embedded content, or inline previews. The official ["stock" example](http://docs.wagtail.org/en/stable/advanced_topics/customisation/extending_draftail.html#creating-new-entities) is one of those. Here is what its attached quote card looked like on the Forbes website:
[](https://www.forbes.com/sites/jasonbloomberg/2018/02/04/the-real-reason-red-hat-is-acquiring-coreos/#70a79bf05c4d)
> Quote cards enrich Forbes articles with stock information, and also show related content that readers might find helpful.
### Tokens, IMEs, autocompletes
By "token", I mean any content that could be confused with other plain text if it did not have a [particular meaning](https://en.wikipedia.org/wiki/Lexical_analysis#Token). IMEs and autocompletes are simply ways to insert content. Some examples include:
- Emojis, with an emoji picker, and potentially an interface to enter alternative labels for screen readers.
- Hashtags, with or without an aucomplete or other IME.
- Predefined keywords, tags, or entries from a taxonomy.
- Mentions, most likely with an autocomplete that turns them into links.
- Any set of values specific to the site’s purpose. For example, a web design website might want a way to easily insert color codes into their content: `#E75480`.
[](https://www.draft-js-plugins.com/plugin/mention)
> [Draft.js plugin](https://www.draft-js-plugins.com/plugin/mention) for a mention feature, where `@` triggers the autocomplete UI.
### Editor metrics, highlighting, helpers
Anything that helps the end user without necessarily changing the content. The folks from VIX Digital have some great examples from this category: [vixdigital/draftail-plugins](https://github.com/vixdigital/draftail-plugins).
- Text metrics – content length, readability, reading time, and much more.
- Highlighting – be it syntax highlighting for programmers, or highlighting of specific words that are particularly important in the content.
- Spellcheckers and writing assistants. They will highlight content, as well as offer alternative text.
[](https://vixdigital.github.io/draftail-plugins/)
> The Reading Level plugin from VIX Digital is a great example. The metrics help you understand your content better, without interrupting the writing flow.
> Better yet, it updates as you type, and is helpful regardless of whether your content is rich text or not.
## Building a content extension
You’re ready to wrangle with rich text. StreamField definitely won’t cut it for your specific problem. The examples above sound similar to your use case. Here are initial considerations before you get building:
- **How will the extension behave in the editor?**
- Will it be an additional toolbar button? What happens when you click on it?
- If you create custom entities (say a `STOCK`), how are they meant to behave once inserted in the editor? Can they be edited? Deleted?
- What does the your custom content look like when displayed in the editor?
- **How will the custom format be stored within rich text?**
- Will it have its own HTML tag, perhaps with specific attributes?
- Will it risk clashing with the storage format of other rich text content?
- Which parts of your rich text markup are semantic? Which are presentational? Can those not be stored?
- Is there a potential need for migration of this content in the future? Can you make this easier on yourself?
- **What will the content look like on the site’s front-end?**
- Will you need the storage to be done in a specific way to render a no-JS fallback?
- What will the differences be between stored content and rendered content?
Some of those questions can be hard to answer if you don’t have experience building rich text extensions. My preferred approach is to get prototyping and build a [proof of concept (PoC)](https://en.wikipedia.org/wiki/Proof_of_concept).
### Required knowledge
Most of the time spent developing extensions will be spent with the APIs of [Draft.js](https://draftjs.org/), the framework that Draftail [is built upon](../introduction/getting-started.md#why-we-need-draftjs-and-react). The Draftail documentation is a good resource to learn what types of extension are supported and general high-level concepts, but in order to develop an extension that manipulates the editor’s content, you will likely need to read the Draft.js docs – or search for examples of similar extensions built for Draft.js itself, which are likely reusable.
### Prototyping extensions
Generally, the hardest part to build when creating a content extension is the editing UI (think: choosers as forms within modals, tooltips). Then, content storage and conversion is the most important part to "get right" from the beginning, since changes there will create stale content that can be hard to deal with. Here are my tips:
1. Get your extension to display in the toolbar, and use [`window.prompt()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt) as a crude data entry modal.
2. Make a simple decorator component displaying your custom content as bold (or any other very basic style) to see where your data is.
3. Play with content conversion and storage, and get this right as soon as possible.
4. Quickly prototype the front-end rendering of your new rich text content.
## Going further
[Draft.js Plugins](https://github.com/draft-js-plugins/draft-js-plugins) also offers a lot of general-purpose extensions for Draft.js. For Draftail in particular, as of today, the most advanced extensions to learn from are Wagtail’s built-in [Link, Document, Image, and Embed](https://github.com/wagtail/wagtail/blob/607f2ec0673814a54bd8c35f7cda42a4b37f73f2/client/src/components/Draftail/decorators/Link.js). They use the same APIs as any custom extension would, and offer quite good rich text interactions.
---
## Importing and exporting HTML
Like all Draft.js editors, Draftail does not process HTML directly: It uses its own content representation. [Content storage](../reference/content-storage.md) can be done with the Draft.js representation, but it is also possible to import and export HTML instead.
## Deciding how to store content
In some use cases, it may be desirable to store rich text as HTML. This is particularly useful for websites which do not need further processing of their content when displaying it, or which already have existing HTML processing (for example in a CMS). There are also scenarios in which it might be better to store content with the Draft.js [ContentState](https://draftjs.org/docs/api-reference-content-state/) representation, for example if the content is meant to be used in different mediums (web, mobile apps, email, etc).
**In either case, Draftail has no preference as long as it is provided with raw ContentState when initialised with [`rawContentState`](../reference/api.md#rawcontentstate-and-onsave), or EditorState when using [`editorState`](../reference/api.md#editorstate-and-onchange).**
There are a lot of tools available to convert content. We built the Python [Draft.js exporter](https://github.com/springload/draftjs_exporter) while working on Draftail, for use in Python backends. For the purpose of this guide, we will be doing the conversion in the browser with [draft-convert](https://github.com/HubSpot/draft-convert).
> There are many more converter options available on [Awesome Draft.js](https://github.com/nikgraf/awesome-draft-js).
## Draft.js content conversion
The key is to make sure that the content converters and the editor all use the same identifiers for formatting types: [inline styles](../introduction/inline-styles.md), [blocks](../introduction/blocks.md), and [entities](../introduction/entities.md), and that they all preserve the same attributes/props when needed.
For common formats, Draft.js has its predefined identifiers: [block types](https://github.com/facebook/draft-js/blob/master/src/model/constants/DraftBlockType.js), [inline styles](https://github.com/facebook/draft-js/blob/master/src/model/immutable/DefaultDraftInlineStyle.js), and `LINK` and `IMAGE` for entities. Draftail [exposes the same identifiers](../reference/api.md#content-format-identifiers) (and some more) for convenience, although using the exact same string everywhere will also work.
```js
console.log(INLINE_STYLE.BOLD)
```
### HTML import
Let's use [`convertFromHTML`](https://github.com/HubSpot/draft-convert#convertfromhtml) from `draft-convert`. We can configure it to use the same identifiers as Draftail when converting HTML.
```jsx
const content = `
This editor demonstrates HTML import and export.
Built with draft-convert
`
const importerConfig = {
htmlToEntity: (nodeName, node, createEntity) => {
// a tags will become LINK entities, marked as mutable, with only the URL as data.
if (nodeName === "a") {
return createEntity(ENTITY_TYPE.LINK, "MUTABLE", { url: node.href })
}
if (nodeName === "img") {
return createEntity(ENTITY_TYPE.IMAGE, "IMMUTABLE", {
src: node.src,
})
}
if (nodeName === "hr") {
return createEntity(ENTITY_TYPE.HORIZONTAL_RULE, "IMMUTABLE", {})
}
return null
},
htmlToBlock: (nodeName) => {
if (nodeName === "hr" || nodeName === "img") {
// "atomic" blocks is how Draft.js structures block-level entities.
return "atomic"
}
return null
},
}
const fromHTML = (html) => convertToRaw(convertFromHTML(importerConfig)(html))
const editor = (
)
```
`convertFromHTML` does the heavy lifting, followed by the Draft.js [`convertToRaw`](https://draftjs.org/docs/api-reference-data-conversion#converttoraw), and we can then initialise Draftail with HTML.
### HTML export
Converting back to HTML is the inverse process, with [`convertToHTML`](https://github.com/HubSpot/draft-convert#converttohtml). It also needs to use the same identifiers as Draftail when converting HTML, and we will want to make sure the importer and exporter can be used in succession without altering the content.
```jsx
const exporterConfig = {
blockToHTML: (block) => {
if (block.type === BLOCK_TYPE.BLOCKQUOTE) {
return
}
// Discard atomic blocks, as they get converted based on their entity.
if (block.type === BLOCK_TYPE.ATOMIC) {
return {
start: "",
end: "",
}
}
return null
},
entityToHTML: (entity, originalText) => {
if (entity.type === ENTITY_TYPE.LINK) {
return {originalText}
}
if (entity.type === ENTITY_TYPE.IMAGE) {
return
}
if (entity.type === ENTITY_TYPE.HORIZONTAL_RULE) {
return
}
return originalText
},
}
const toHTML = (raw) =>
raw ? convertToHTML(exporterConfig)(convertFromRaw(raw)) : ""
const editor = (
{
console.log(toHTML(raw))
}}
enableHorizontalRule
entityTypes={[
{
// We use the same value for type as in the converter.
type: ENTITY_TYPE.LINK,
source: LinkSource,
decorator: Link,
// We define what data the LINKs can have.
attributes: ["url"],
allowlist: {
href: "^(?![#/])",
},
},
]}
/>
)
```
Again, most of the configuration work is with `convertToHTML`, but we also need the Draft.js [`convertFromRaw`](https://draftjs.org/docs/api-reference-data-conversion#convertfromraw) to read content from Draftail.
## Demo
Here is a demo that initialises from HTML, and converts content to HTML on save (logged in the browser DevTools console).
---
## Browser support and polyfills
**Supported browser / device versions:**
| Browser | Device/OS | Version |
| ------- | -------------- | ------- |
| Chrome | Windows, macOS | Last 2 |
| Firefox | Windows, macOS | latest |
| Firefox | Windows, macOS | ESR |
| MS Edge | Windows | Last 2 |
| Safari | macOS | Last 3 |
**Partial support:**
| Browser | Device/OS | Version |
| ------------- | ---------- | ------- |
| Mobile Safari | iOS Phone | Last 2 |
| Mobile Safari | iOS Tablet | Last 2 |
**Unsupported:**
| Browser | Device/OS | Version |
| ------- | --------- | ------- |
| IE11 | Windows | latest |
| Chrome | Android | latest |
## Right-to-left languages
Draftail supports right-to-left languages.
## JavaScript
Draftail requires JavaScript to work. We would encourage integrators of the editor to include a ["please enable JavaScript"](https://www.enable-javascript.com/) message in a `