Hire the author: Hadijah K

In this article, we are going to create a react app and add a rich text editor using the draftjs library and react-draft-wysiwyg. Rich text editors are important in applications because of their extended functionalities and text formattings.

There are a number of frameworks that one can use for rich text editors but we shall choose draft.js because of its precedence over the others.

  • Draft.js is a framework for building rich text editors in React, powered by an immutable model and abstracting over cross-browser differences.
  • Draft.js allows you to build any type of rich text input, whether you’re only looking to support a few inline text styles or building a complex text editor for composing long-form articles.

Image source: https://i.ytimg.com/vi/1wS_jGp1UfM/maxresdefault.jpg

Glossary

  • Wysiwyg – A WYSIWYG (pronounced “wiz-ee-wig”) editor or program is one that allows a developer to see what the end result will look like while the interface or document is being created. WYSIWYG is an acronym for “what you see is what you get”.
  • CDN – A content delivery network, or content distribution network, is a geographically distributed network of proxy servers and their data centers. The goal is to provide high availability and performance by distributing the service spatially relative to end users. In this case we are using the bootstrap CDN link.

Prerequisites

  • Nodejs installed on your machine.
  • Git installed on your machine.
  • Yarn or npm installed.
  • Some knowledge about React Apps(if you are a beginner still don’t worry, try to catch up).

What we expect to achieve by the end

  • Having a Controlled editor component with the conversion of content from and to HTML. A Wysiwyg Built on ReactJS and Draft-JS.
  • Right below the editor, we shall have a representation of the underlying HTML that is generated by the editor. When one clicks the preview button, you see the parsed HTML(how it actually looks).
editor
Underlying html
react-draft-wysiwyg
Output

Create a react app first

Go into the directory where you want to keep your application, maybe desktop or downloads or anywhere.

  • In the command line, type this
npx create-react-app myapp
  • Once it is successful, you should see.
  • When you open the application in a text editor like Vscode or sublime, the folder structure should be as follows.
myapp-|
|__node_mudles/
|__public/
|__src/
|__.gitignore
|__pacakge.json
|__README.md
|__yarn.lock

Add draft-js and react-draft-wysiwyg to the app.

In the myapp directory, install draft-js and react-draft-wysiwyg by running,

npm install -S draft-js react-draft-wysiwyg or yarn add draft-js react-draft-wysiwyg

Add the editor component to our app.

Navigate to the src folder of the app, create a new folder named components and in that folder add a file called myEditor.js which will have the code for our reusable text editor.

  • Add the following code and render the text editor in the App component. As a result, you will see a rich text editor on your page. Though it’s not yet that rich, isn’t it? so, let’s add some styles to the app so that everything is visible.
import React, { Component } from 'react';
import { EditorState } from 'draft-js';
import { Editor } from 'react-draft-wysiwyg';
class MyEditor extends Component {
constructor(props) {
super(props);
this.state = {
editorState: EditorState.createEmpty()
};
}
onEditorStateChange = editorState => {
this.setState({
editorState
});
};
render() {
const { editorState } = this.state;
return (
<div>
<Editor
editorState={editorState}
wrapperClassName="rich-editor demo-wrapper"
editorClassName="demo-editor"
onEditorStateChange={this.onEditorStateChange}
placeholder="The message goes here..."
/>
</div>
);
}
}
export { MyEditor };
view raw myEditor-1.js hosted with ❤ by GitHub
  • In our App.js file, react already gives us some default code but we shall replace it with the code below. Note that we also import the react-draft-wysiwyg.css from node_modules so that we get all the styles for our editor.
import React from 'react';
import { MyEditor } from './components/myEditor';
import './App.css'
import '../node_modules/react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
function App() {
return (
<div className="app">
<MyEditor />
</div>
);
}
export default App;
view raw App.js hosted with ❤ by GitHub
  • When we run yarn start in the terminal and it will open our React app at localhost:3000 however the styles still look off, so we need to add some styles to our components. In the file, App.css add the code below.
.app {
text-align: center;
background-color: #2a2a2ab5;
height: 100vh;
}
.app .rich-editor {
padding: 10rem;
}
.rich-editor .rdw-editor-main {
background-color: #fff;
}
.rich-editor .DraftEditor-editorContainer {
padding: 1rem;
}
.draft-editor-wrapper {
border: 1px solid #ccc;
}
view raw App-0.css hosted with ❤ by GitHub
  • Now that we can see our rich text editor, play around with it by typing in some words and styling them. There is one more important thing that we need to do, get that typed word from the editor, present it in our App.

Data conversion

  • Let’s get fancier here, we are going to get our content from the text editor and present it in our preview modal. We use libraries like draft-js-to-Html and an element prop  dangerouslySetInnerHTMLwhich will interpret the Html and render it the way it is.
  • Add a bootstrap button for preview in myEditor.js and a div to view the HTML as we type, it won’t do anything for now but it’s what we shall use to preview our results from the text editor.
  • Remember to add the bootstrap CDN links and scripts to the index.html of the app in the public folder.
import React, { Component } from 'react';
import { EditorState } from 'draft-js';
import { Editor } from 'react-draft-wysiwyg';
class MyEditor extends Component {
constructor(props) {
super(props);
this.state = {
editorState: EditorState.createEmpty()
};
}
onEditorStateChange = editorState => {
this.setState({
editorState
});
};
render() {
const { editorState } = this.state;
return (
<div>
<Editor
editorState={editorState}
wrapperClassName="rich-editor demo-wrapper"
editorClassName="demo-editor"
onEditorStateChange={this.onEditorStateChange}
placeholder="The message goes here..."
/>
{/*new code: adding a bootstrap button and preview div, remember to remove these comments*/ }
<h4>Underlying HTML</h4>
<div className="html-view">
...
</div>
<button className="btn btn-success" data-toggle="modal" data-target="#previewModal">
Preview message
</button>
{/* end of new code*/ }
</div>
);
}
}
export { MyEditor };
view raw myEditor-2.js hosted with ❤ by GitHub

In index.html, add some code blocks.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!-- Bootstrap link below-->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!-- bootstrap Scripts added below-->
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
view raw index.html hosted with ❤ by GitHub

Now that we have a button that does nothing let’s add a bootstrap modal in a new file inside the components folder called previewModal.js that it will pop.

import React from 'react';
export const PreviewModal = () => (
<div class="modal fade" id="previewModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">
Preview Modal
</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body" />
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Done
</button>
</div>
</div>
</div>
</div>
);

Let’s ensure that we are on the same page as far as the folder structure is concerned.

myapp-|
|__node_mudles/
|__public/
|__index.html
|__favicon.ico
|__logo192.png
|__logo521.png
|__robot.txt
|__src/
|__components/
|__myEditor.js
|__previewModal.js
|__App.css
|__App.js
|__App.test.js
|__index.css
|__index.js
|__serviceWorker.js
|__setupTest.js
|__.gitignore
|__pacakge.json
|__README.md
|__yarn.lock

We then import our modal and render it in the editor component as follows.

When you click the previewMessage button, it should open a modal with nothing. Let’s give it what to present.

First, install draftjs-to-html dependency by running yarn add draftjs-to-html . Import it in myEditor file and use it to make conversions. Also, add convertToRaw from draft-js

import React, { Component } from 'react';
import { EditorState, convertToRaw } from 'draft-js'; {/* new import added*/}
import draftToHtml from 'draftjs-to-html'; {/* new */}
import { Editor } from 'react-draft-wysiwyg';
import { PreviewModal } from './previewModal';
const getHtml = editorState => draftToHtml(convertToRaw(editorState.getCurrentContent())); {/* new */}
class MyEditor extends Component {
constructor(props) {
super(props);
this.state = {
editorState: EditorState.createEmpty()
};
}
onEditorStateChange = editorState => {
this.setState({
editorState
});
};
render() {
const { editorState } = this.state;
return (
<div>
<Editor
editorState={editorState}
wrapperClassName="rich-editor demo-wrapper"
editorClassName="demo-editor"
onEditorStateChange={this.onEditorStateChange}
placeholder="The message goes here..."
/>
<h4>Underlying HTML</h4>
<div className="html-view">
{getHtml(editorState)}
</div>
<button className="btn btn-success" data-toggle="modal" data-target="#previewModal">
Preview message
</button>
<PreviewModal output={getHtml(editorState)} /> {/* updated with output */}
</div>
);
}
}
export { MyEditor };
view raw myEditor.js hosted with ❤ by GitHub

Update the previewModal.js file by adding the output to our body and using the dangerouslySetInnerHTML prop. The updated file looks like the one below.

import React from 'react';
export const PreviewModal = ({ output }) => (
<div class="modal fade" id="previewModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">
Preview Modal
</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body" dangerouslySetInnerHTML={{ __html: output }} />
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Done
</button>
</div>
</div>
</div>
</div>
);
view raw previewModal.js hosted with ❤ by GitHub

We are almost done, just a little bit of styling left, let’s add a few styles to our App.css and the test our app.

.app {
text-align: center;
background-color: #2a2a2ab5;
height: 100vh;
}
.app .rich-editor {
padding: 10rem;
}
.rich-editor .rdw-editor-main {
background-color: #fff;
}
.rich-editor .DraftEditor-editorContainer {
padding: 1rem;
}
.draft-editor-wrapper {
border: 1px solid #ccc;
}
.html-view {
background-color: #fff;
color: #000;
margin: auto;
width: 70%;
margin-bottom: 2rem;
}
h4 {
color: #fff;
}
view raw App.css hosted with ❤ by GitHub

If everything is running well, take a deep breath and smile 😀, you have implemented a rich text editor, been able to get data from it and present it in the React App. If your app is not running you can start it by running yarn start in the terminal, ensure to be at the root of myapp

Find a finished application on Github

Reflection

  • With the ability to add such a react-draft-wysiwyg rich text editor using draft-js in your React App, you will improve your User Interface as well as User experience.

Learning strategy

  • While learning about rich text editors, the documentations were not enough to make my User Interface look like what I wanted.
  • So I used my knowledge as a frontend engineer to customise the editor using my own styles. Lesson to learn is that not all can be got from the documentation, you need to be creative some times.

Conclusion and learning resources

Hire the author: Hadijah K