Top 5 Rich-Text React Components
Content is at the heart of web interfaces. Rich text makes up the crust of creating text content with differently formatted parts.
In this article, we will illustrate five rich text components for creating content in React applications.
We will also look at the pros and cons of each component.
Lastly, we will discuss the future of the dynamic presentation of content using Portable Text and how to use Portable Text from Sanity Studio.
Rich text is used in various forms of presentations on the web, including blog posts, articles, listings, and more robust interfaces like e-commerce product descriptions and social media.
Below are 5 top rich text components for React.js.
Draft.js
Draft.js is a robust, extensible, and customizable React.js framework for building rich text editors. It provides the building blocks for building rich text inputs with an immutable approach to managing data.
Draft.js follows the same paradigm as controlled components in React and provides an Editor
component that renders a rich text input.
Draft.js exposes an EditorState
API to handle/store state updates in the Editor
component.
Installation
Draft-js requires React and React-DOM. We install them using npm or yarn with:
npm install draft-js react react-dom
yarn add draft-js react react-dom
Usage
import {Editor, EditorState} from 'draft-js'
const RichtextEditor = () => {
const [editorState, setEditorState] = useState(()=> EditorState.createEmpty())
return (
<div>
<Editor editorState={editorState} onChange={setEditorState}/>
</div>
)
}
The onChange
handler overwrites the editorState
data with the new data in the editor. editorState
holds an immutable Record with all changes and events in the editor and is simply a snapshot of its state.
Draft.js provides props to manage various configurations, including editor styling on event triggers and block styling for singular rich text entities like headers and blockquotes.
With the content created in the editor, we want to convert this to HTML, which we can display on a page. There are libraries to handle the conversion of this data, including draft-convert and draftjs-to-html.
Pros
- Robust and customizable data on a document level and lower level into blocks of various text elements
- Uses immutable-js to manage performant state updates
- Supports custom controls
- Ships text directions for RTL languages and spell-checker
- EditorState contains undo/redo stacks and any other action done on the editor
Cons
- Requires setup from scratch, plus controls to set up a full-fledged editor
- Requires a parser installed to render entered markup ## React-Draft- WYSIWYG
React-draft-wysiwyg is an editor built on Draft.js. Suppose you don’t want the hassles of building your own rich text editor UI from scratch. In that case, react-draft offers a fully fitted editor with options to customize the editor even further.
React-draft also provides the ability to use the editor as a controlled or uncontrolled component. React-draft provides the option to customize the toolbar options and add custom react components to it.
Installation
React-draft depends on Draft.js, React, and react-dom. We install React-draft using npm or yarn with:
npm install react-draft-wysiwyg draft-js react react-dom
yarn add react-draft-wysiwyg draft-js react react-dom
Usage
With react-draft, EditorState
, an immutable Record of the editor’s state, is imported from draft-js and Editor
from react-draft-wysiwyg
.
Here’s usage on a React page:
import React, { useEffect, useState } from "react";
import { Editor } from "react-draft-wysiwyg";
import { EditorState } from "draft-js";
import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";
export default function App() {
const [editorState, setEditorState] = useState(() =>
EditorState.createEmpty()
);
useEffect(() => {
console.log(editorState);
}, [editorState]);
return (
<div>
<h1>React Editors</h1>
<h2>Start editing to see some magic happen!</h2>
<div style={{ border: "1px solid black", padding: '2px', minHeight: '400px' }}>
<Editor
editorState={editorState}
onEditorStateChange={setEditorState}
/>
</div>
</div>
);
}
The resulting editor looks like this:
The above code snippet shows the usage of React-Draft-WYSIWYG as a controlled component. In an uncontrolled behavior, the initialEditorState
prop is used instead of the editorState
prop in <Editor/>
.
Pros
- Provides ready-made UI out of the box
- Allows for UI enhancements and customizations, including emoji support
- Accepts props of CSS class names for speedy editor styling
- Easily set up hashtags and mentions with suggestions from a dataset
Cons
- Requires a parser to convert
EditorState
to HTML or any other markup. - Parsers for draft-js to HTML or any additional markup could be inadequate in handling the different block/element types.
React Quill
Quill.js is a fast and lightweight rich text editor built with cross-platform and cross-browser support in mind.
Its strength also lies in its extensibility and configurability using themes.
React-Quill is a Quill component for React with support for TypeScript. React-Quill ships with a full-featured editor with the option to customize the toolbars and set up themes.
React-Quill is seamless to integrate. React-quill touts a hybrid input of controlled and uncontrolled behavior, using the component's value
prop bound to its state.
React-Quill also features an uncontrolled mode that uses
defaultValue
to instantiate the editor data.
A theme specification and a function passed to the onChange
prop of the component are only required to render the editor and handle data input.
React-Quill outputs HTML and can be used in JSX elements with dangerouslySetInnerHTML
.
Installation
React-quill is installed via npm or yarn with:
npm install react-quill
yarn add react-quill
Usage
Import the React-quill component along with the theme required. The default theme Snow is used when a theme is not specified.
import ReactQuill from "react-quill"
import 'react-quill/dist/quill.snow.css'
export default function App() {
const [convertedText, setConvertedText] = useState("Some default content");
return (
<div>
<ReactQuill
theme='snow'
value={convertedText}
onChange={setConvertedText}
style={{minHeight: '300px'}}
/>
</div>
);
}
Pros
- Allows complete toolbar customization with HTML & JSX elements support
- Ease of setup and use
- It outputs HTML, so it serves simpler use cases like blog posts and content presentation layers with precise data requirements.
- Theming support for preset editor styling
Cons
- Limited customization of content blocks
- Security vulnerabilities of primarily rendering HTML
Slate
Slate.js, currently in beta, is a framework for building robust, rich text editors. Slate is made to be highly extensible, thus improving its native capabilities to create rich text editors. Slate is built with inspiration from tools including Quill and Draft.js.
Slate poses to solve several bottlenecks with managing rich text content, some we have seen previously in this post.
Slate aims to solve these challenges:
- Serialization to HTML & Markdown doesn’t ship out of the box
- Collaborative content creation is an afterthought
- Restrictive schema definition of document models
- Dynamic content creation should transcend beyond text, links, and media content
Installation
Slate is distributed as a monorepo and can be installed along with its React plugin using npm or yarn with:
npm install slate slate-react
yarn add slate slate-react
Slate also requires the installation of react
and react-dom
as peer dependencies.
Usage
A Slate editor’s essential representation is a fundamental contentEditable
element, customized further until the desired functionality is achieved.
To use Slate, we import the slate editor composer and components from its React plugin.
import React, { useState, useMemo } from "react";
import { createEditor } from "slate";
import { Slate, Editable, withReact } from "slate-react";
In the imports we have:
Slate: A context provider component for the Slate editor. It’s a controlled component that tracks the full editor state and updates.
Editable: Renders an editable rich text document, similar to contentEditable
.
withReact: Provides the editor with React specific functionalities
Creating an <SlateEditor/>
component and rendering a simple editor, we have:
import React, { useState, useMemo } from "react";
import { createEditor } from "slate";
import { Slate, Editable, withReact } from "slate-react";
import "./styles.css";
export default function SlateEditor() {
const editor = useMemo(() => withReact(createEditor()), []);
const [value, setValue] = useState([
{
type: "paragraph",
children: [{ text: "We have some base content." }]
}
]);
return (
<div className="App">
<h1>React Editors</h1>
<h2>Start editing to see Slate in action!</h2>
<Slate
editor={editor}
value={value}
onChange={(newValue) => setValue(newValue)}
>
<Editable style={{ border: "1px solid black" }}/>
</Slate>
</div>
);
}
useMemo
hook maintains a consistent editor object during the component update. We initialized the Slate
controlled component’s state data with an array containing an object with a block and children.
Slate
uses the default renderer, which outputs a div
to render the default paragraph content. The editor can be extended further using events, custom renderers, custom elements, and commands to include controls, filters, and much more.
You can find out more on using Slate to build a fully-featured rich text editor similar to Medium and dropbox paper here.
Pros
- Ships robust APIs and handlers to build out fully featured rich text editors
- Dynamic content blocks with types to further customize or abstract portions of the content
- Outputs plain JSON; hence serialization to other data formats is seamless
- Extensible with the use of plugins
Cons
- Requires setup with a steep learning curve to handle simple content use cases
- Requires UI setup to operate controls in the editor
Jodit-React
Jodit is an open-source WYSIWYG editor written in TypeScript. Jodit-react, a wrapper for Jodit, is a great WYSIWYG rich text editor that ships with controls to handle most rich text formatting, links, and tables.
Installation
Install Jodit and jodit-react using npm and yarn with:
npm install jodit jodit-react
yarn add jodit jodit-react
Usage
Here’s the sample usage below to render a rich text editor with default controls and a handler to update the component state using the onBlur
event.
import React, { useState, useRef } from "react";
import JoditEditor from "jodit-react";
import "./styles.css";
export default function App() {
const editor = useRef(null);
const [content, setContent] = useState("Start writing");
const config = {
readonly: false,
height: 400
};
const handleUpdate = (event) => {
const editorContent = event.target.innerHTML;
setContent(editorContent);
};
return (
<div className="App">
<h1>React Editors</h1>
<h2>Start editing to see some magic happen!</h2>
<JoditEditor
ref={editor}
value={content}
config={config}
onBlur={handleUpdate}
onChange={(newContent) => {}}
/>
<div dangerouslySetInnerHTML={{ __html: content }} />
</div>
);
}
We imported the required modules and set up a basic config for the editor. You can find more editor config options here.
We proceed to create a function to handle state updates with data from the editor. <JoditEditor/>
renders the editor, which looks like this:
Pros
- Provides themes and custom theme creation
- Easy to setup WYSIWYG editor for simple content requirements
- Provides custom controls and buttons to enhance the editor
- Allows the creation of custom plugins for editor extensibility
Cons
- Absence of block content with types for more in-depth content control
- It doesn’t support robust data requirements like embeds and collaborative content creation
- The content output is in HTML as typical with WYSIWYG editors, with potential security vulnerabilities when rendering the data using
dangerouslySetInnerHTML
in React.
Portable Text
Portable text is a JSON-based open specification with a renewed approach to handling and presenting rich text in modern applications. Portable text is created to solve challenges in creating rich content and its presentation in various differing interfaces.
Portable text content can be serialized into any content format. Its customizable and extensible data structure serves a limitless approach to constructing multi-level content either with data entities as siblings or children.
Portable text returns content in the form of an array containing blocks of child content with a style, types, and mark definitions - these are formats applied to content types. The JSON formatted portable text content is further transformed into any standard data format, including HTML and Markdown with serializers.
Sanity Studio
Sanity Studio is an open-source CMS with real-time collaboration on modern data requirements. Sanity utilizes portable text to serve block content created in the Sanity Studio. Portable text and the structured content approach of the Sanity Studio allow users to create various content models bordered on solving both domain and generalized content problems.
Sanity Studio also offers the ability to extend content solutions with plugins, integrations, and interfaces.
Sanity studio installation
Sanity has multiple official starters to get started on a project quickly. These include starters for JAMStack frameworks like Gatsby.js, Next.js, Eleventy, and Gridsome. There are starters for Blogs, E-commerce, Portfolio website, and a landing page with data from Sanity Studio. We can find all starters here and even community starters.
Alternatively, we can create a new project from scratch using sanity init
.
To do this, install sanity CLI globally with:
npm install -g @sanity/cli
On sanity CLI installation completion, proceed to create a Sanity account or login from the CLI with:
sanity login
Once we log in, we run sanity init
, follow the CLI prompts to create a new project. We’ll choose the default dataset configuration and any of the project templates. Here, we choose the blog template that ships with the schema.
With the project’s successful setup, we change the directory into the project folder and run sanity manage
to open the project in the browser, and it looks like this:
To open the studio locally, in the project directory, we run:
sanity start
This command creates a local development server on http://localhost:3333. The local studio looks like this with the blog data schema:
In the studio’s Post menu, we click the plus (+) icon on the top right corner to open the blog creation page. The blog editor contains a Portable Text rich text editor for structured block content. We create a sample blog content with title and text content.
We’ll deploy a GraphQL API for the studio. This way, we can query data from the studio. We’ll do this with:
sanity graphql deploy
A GraphQL API is created and deployed to sanity with a default
data tag. We’ll click the presented URL to see the schema in the GraphQL playground. Here’s a sample query to fetch the title
and JSON Portable text content in bodyRaw
of all blog posts:
Sanity Studio Content in Gatsby.js
Gatsby.js is a tool for building super-fast single-page JAMstack applications. To use data from Sanity studio in a gatsby project, we require a source plugin for Gatsby.js. gatsby-source-sanity solves this.
We’ll install it with:
npm install gatsby-source-sanity
In a gatsby project (different from the sanity studio project), we specify the plugin configuration in the plugins array of gatsby-config.js
with:
module.exports = {
plugins: [
[...]
{
resolve: "gatsby-source-sanity",
options: {
projectId: "enter your project id",
dataset: "production || any other dataset on sanity",
token: "enter your sanity read token",
}
}
]
}
Refresh the gatsby development server and open the graphql playground to see the source data from sanity.
We can pull the content we want from sanity into our gatsby project, along with other content created on Sanity.
In Gatsby projects, we use block-content-to-react to serialize Portable Text.
Summary
In this post, we discussed five popular React.js rich text editors. We discussed robust editors with block content to WYSIWYG editors for simple content requirements. Each of these is fit for specific use cases depending on the complexity of the project.
We discussed portable text and the problems it solves in dynamic content creation - lastly, we setup Sanity studio with a blog schema that uses Portable Text. We created a GraphQL API for the content and utilized gatsby-source-sanity
to source the GraphQL data into a Gatsby.js project.