Top 5 Rich-Text React Components
Tags: react, richtext, quill, draft
Published: 2021-03-03 18:07:28

# 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](https://www.sanity.io/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](https://draftjs.org/) 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:
```bash
    npm install draft-js react react-dom
    yarn add draft-js react react-dom
```
**Usage**

```jsx
    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*](https://immutable-js.github.io/) 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](https://jpuri.github.io/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:

```bash
    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](https://immutable-js.github.io/) of the editor’s state, is imported from draft-js and `Editor` from `react-draft-wysiwyg`.

Here’s usage on a React page:

```jsx
    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:


![](https://paper-attachments.dropbox.com/s_E113D0606C08FF5A4201A92119BFD2AFC1066F011E0FDFEAAB76DB2B0CE3D018_1610024395682_image.png)


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](https://quilljs.com/) 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](https://github.com/zenoamaro/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:

```bash
    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.
 
```jsx
    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](https://github.com/ianstormtaylor/slate), 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:

```bash
    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.

```jsx
    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:

```jsx
    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](https://docs.slatejs.org/).

**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](https://xdsoft.net/jodit/) is an open-source WYSIWYG editor written in TypeScript. [Jodit-react](https://github.com/jodit/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:

```bash
    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.

```jsx
    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](https://xdsoft.net/jodit/doc/options/).
We proceed to create a function to handle state updates with data from the editor. `<JoditEditor/>` renders the editor, which looks like this:


![](https://paper-attachments.dropbox.com/s_E113D0606C08FF5A4201A92119BFD2AFC1066F011E0FDFEAAB76DB2B0CE3D018_1610199878199_image.png)


**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](https://github.com/portabletext/portabletext) 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](https://www.sanity.io/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](https://create.sanity.io/?template=sanity-io%2Fsanity-template-gatsby-blog), [E-commerce](https://create.sanity.io/?template=sanity-io%2Fsanity-template-nextjs-ecommerce), [Portfolio website](https://create.sanity.io/?template=sanity-io%2Fsanity-template-gatsby-portfolio), and a [landing page](https://create.sanity.io/?template=sanity-io%2Fsanity-template-kitchen-sink) with data from Sanity Studio. We can find [all starters here](https://create.sanity.io/) and even community starters.

Alternatively, we can create a new project from scratch using `sanity init`. 

To do this, install sanity CLI globally with:

```bash
    npm install -g @sanity/cli
```
On sanity CLI installation completion, proceed to create a Sanity account or login from the CLI with:

```bash
    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:


![](https://paper-attachments.dropbox.com/s_E113D0606C08FF5A4201A92119BFD2AFC1066F011E0FDFEAAB76DB2B0CE3D018_1610202547923_image.png)


To open the studio locally, in the project directory, we run:

```bash
    sanity start
```
This command creates a local development server on http://localhost:3333. The local studio looks like this with the blog data schema:


![](https://paper-attachments.dropbox.com/s_E113D0606C08FF5A4201A92119BFD2AFC1066F011E0FDFEAAB76DB2B0CE3D018_1610202813065_image.png)


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: 

```bash
    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:


![](https://paper-attachments.dropbox.com/s_E113D0606C08FF5A4201A92119BFD2AFC1066F011E0FDFEAAB76DB2B0CE3D018_1610205527205_image.png)


**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: 

```bash
    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:

```js
    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](https://www.npmjs.com/package/@sanity/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.