Building a Rich Text Editor with React and Draft.js, Pt. 1: Basic Set Up

Siobhan Mahoney

This is the first installment of a multi-part series about building a Rich Text Editor with React and Draft.js. Links to additional posts in this series can be found below. The full codebase for my ongoing Draft.js project is available on GitHub. Feel free to checkout the deployed demo here.

Draft.js is a Javascript rich text editor framework designed for React. In this post, I will explain why Draft.js is such a powerful tool and provide a high-level overview of the core features of its API. I will also go over the steps for setting up Draft.js in your React App and provide instructions for adding basic rich text functionality.

Table of Contents:

I. Background

II. Core API Features

III. Basic Set Up

IV. Adding Basic Rich Text Functionality

I. Background

Introduced in 2016, Draft.js was built by Facebook and served to tackle a number of issues that accompanied the use of <textarea> and <div>tags to capture a user’s posts and comments. As noted in a post published on Facebook’s engineering blog, this method was a total nightmare from a developer’s perspective since it required a number of work-arounds to manually track highlighter and cursor positions. On top of that, the lack of metadata captured in plain text led to issues with state and DOM synchronization. The constraints of plain text could also be felt by the enduser, who could not enjoy rich styles nor embed content in their posts and comments.

As a result, the foundation of Draft.js is the immutable data structures that represent its editor state and a handler to receive state updates from within the editor component, mirroring the controlled DOM input API. This single top-level immutable object serves as a snapshot of the full state entailing the contents, cursor, and undo/redo stacks among other values needed to represent the editor.

II. API Core Features

The cornerstone of Draft.js is its Editor React component and its 2 default props, EditorState and onChange.

  • EditorState is a single immutable object that represents a complete snapshot of the state of the editor, including contents, cursor, and undo/redo history. All changes to content and selection within the editor will create new EditorState objects.
  • onChange() is a callback function that implements any changes that occur in the editor DOM with the current EditorState object and provides this updated state value to the top level of the editor core.

III. Basic Set Up

Let’s begin by creating a new React app:

npm install -g create-react-app
create-react-app draftjs-demo
cd draftjs-demo

Now that we’ve initialized our app, let’s install Draft.js:

yarn add draft-js

Now that Draft.js has been added as a dependency, let’s get started setting up our app.

To keep things organized, I’m going create a PageContainer component that I will work from.

To begin, let’s import Draft.js and add the core Editor component, EditorState, and onChange:

  1. Import the Editor and EditorState class from draft-js

2. Render theEditor Component within PageContainer:

3. Setting up EditorState: Create a constructor() that initiates EditorState within the component’s state, sets its content to empty, and pass it to the Editor component as a prop:

4. Define onChange function within the container component and pass to Editor component as prop:

You should now have a functioning text editor rendering in your browser:

IV. Adding Basic Rich Text Functionality

Now that our basic Editor component is up and running, lets start exploring some of the fun rich text styling and decorators Draft.js has to offer!

RichUtils and Key Commands

The RichUtils module offers a number of useful functions for inline and block style options.

SinceRichUtils has information about the core key commands (e.g., Cmd+B (bold), Cmd+I (italic), and Cmd+U(underline)), we can add functionality to handle these commands using thehandleKeyCommand prop, and hook these into RichUtils to apply or remove the desired style.

Here’s how to add it in your app:

  1. Update Draft.js imports to includeRichUtils:
import { Editor, EditorState, RichUtils} from 'draft-js';

2. Define handleKeyCommand method:

handleKeyCommand = (command) => {
const newState = RichUtils.handleKeyCommand(this.state.editorState, command)
if (newState) {
this.onChange(newState);
return 'handled';
}
return 'not-handled';
}

Here, we are passing a command (like bold or underline) as an argument, which will get passed to the RichUtils.handleKeyCommand, which handles key commands out of the box, along with the current EditorState object. If an updated EditorState is returned as a result, we will then pass the updated new EditorState object to the onChange method, which in turn will update the state.

3. Finally, we pass the handleKeyCommand to the editor component as a prop in order for it to function within our text editor.

<Editor
editorState={this.state.editorState}
handleKeyCommand={this.handleKeyCommand}
onChange={this.onChange}
/>

For reference, here is how your container component should look after following these steps:

RichUtils Buttons

So we can toggle text style via key commands. Awesome. But wouldn’t a button for these commands make for a richer user experience?

Answer: Of course it would, which is why the RichUtils module includes the static method toggleInlineStyle that will take care of it.

To create inline style buttons, create a function for each style button. Each function should pass the editorState object and the type of style command to RichUtils.toggleInlineStyle, which in turn will get passed to onChange. Here’s an example that will italicize the selected text:

onItalicClick = () => {
this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, 'ITALIC'))
}

Then, create a button with an event handler that takes, e.g., onItalicClick as a callback function.

<button onClick={this.onItalicClick}>
<em>I</em>
</button>

Here is how the container element will appear after adding buttons for underlining, italicizing, and bolding fonts:

Curious about what else you can do with Draft.js? Checkout the other posts in this series!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade