Inline and block styles aren't the only kind of rich styling that we might want to add to our editor. The Facebook comment input, for example, provides blue background highlights for mentions and hashtags.
To support flexibility for custom rich text, Draft provides a "decorator" system. The tweet example offers a live example of decorators in action.
The decorator concept is based on scanning the contents of a given ContentBlock for ranges of text that match a defined strategy, then rendering them with a specified React component.
You can use the
CompositeDecorator class to define your desired
decorator behavior. This class allows you to supply multiple
objects, and will search through a block of text with each strategy in turn.
Decorators are stored within the
EditorState record. When creating a new
EditorState object, e.g. via
EditorState.createEmpty(), a decorator may
optionally be provided.
Under the hood
When contents change in a Draft editor, the resulting
EditorStateobject will evaluate the new
ContentStatewith its decorator, and identify ranges to be decorated. A complete tree of blocks, decorators, and inline styles is formed at this time, and serves as the basis for our rendered output.
In this way, we always ensure that as contents change, rendered decorations are in sync with our
In the "Tweet" editor example, for instance, we use a
searches for @-handle strings as well as hashtag strings:
This composite decorator will first scan a given block of text for @-handle matches, then for hashtag matches.
The strategy functions execute the provided callback with the
end values of the matching range of text.
For your decorated ranges of text, you must define a React component to use
to render them. These tend to be plain
span elements with CSS classes or
styles applied to them.
In our current example, the
CompositeDecorator object names
HashtagSpan as the components to use for decoration. These are basic
The Decorator Component will receive various pieces of metadata in
including a copy of the
entityKey if there is one, and the
blockKey. For a full list of props supplied to a Decorator Component see the
props.children is passed through to the rendered output. This is
done to ensure that the text is rendered within the decorated
You can use the same approach for links, as demonstrated in our link example.
The decorator object supplied to an
EditorState need only match the expectations
Flow type definition, which means that you can create any decorator classes
you wish, as long as they match the expected type -- you are not bound by
Setting new decorators
Further, it is acceptable to set a new
decorator value on the
on the fly, during normal state propagation, through immutable means.
This means that during your app workflow, if your decorator becomes invalid or
requires a modification, you can create a new decorator object (or use
null to remove all decorations) and
EditorState.set() to make use of the new
For example, if for some reason we wished to disable the creation of @-handle decorations while the user interacts with the editor, it would be fine to do the following:
ContentState for this
editorState will be re-evaluated with the new
decorator, and @-handle decorations will no longer be present in the next
Again, this remains memory-efficient due to data persistence across immutable objects.