How to build a rich text editor for your React app with CKEditor 5

A WYSIWYG editor provides a rich text editing experience for users, allowing them to easily format text, add images and links, and more. In this article, you will learn how to build a WYSIWYG editor for a React application using CKEditor 5.

Creating a dynamic, user-friendly interface for your React application can be a challenging task. One of the most important aspects of any application is the ability of users to easily create and edit content. That's where a WYSIWYG (What You See Is What You Get) editor comes in. A WYSIWYG editor provides a rich text-editing experience for users, allowing them to easily format text, add images and links, and more.

In this article, you will learn how to build a WYSIWYG editor for your React application. With this, you can go as far as creating something like Google Docs for your application. If you are trying to build a blog, you will not need to touch the code when you want to publish an article. Here, we will build a note application that stores its data on the local storage and then displays the created notes.

You can find the complete code on GitHub.

Introduction to CKEditor 5

CKEditor 5 is a JavaScript-based rich text editor that provides a user-friendly interface for creating and editing content. It is designed to be easily integrated into web applications and can be customized to suit the specific needs of a project. CKEditor 5 offers a wide range of features, such as text formatting, media embedding, undo/redo, and more. It also supports a variety of languages and can be used on both desktop and mobile devices. It's an open-source project that can be easily integrated into your web application and customized according to your needs.

Features of CKEditor 5

CKEditor 5 has a variety of features that make it a powerful and versatile text editor. Here are some of the main features:

  • Advanced text formatting: CKEditor 5 allows users to apply a wide range of text formatting options, including bold, italic, underline, and more.
  • Media embedding: Users can easily insert images, videos, and other media into their content and align, resize, and caption them.
  • Undo/Redo: Users can easily undo and redo any changes they make to their content.
  • Customizable interface: CKEditor 5 has a customizable interface that can be tailored to the specific needs of a project.
  • Collaboration: CKEditor 5 has a built-in collaboration feature, allowing multiple users to work on the same document in real time.
  • Plugins and add-ons: CKEditor 5 has a wide range of available plugins and add-ons, which can be used to extend its functionality.
  • Multilanguage support: CKEditor 5 supports a variety of languages, making it easy to use for users around the world.
  • Mobile support: CKEditor 5 is fully responsive and can be used on both desktop and mobile devices.

Developing the editor for the React app

This code is a React component called "App" that creates a simple note-taking application. The component uses the CKEditor library to provide a rich text editor for users to create and edit notes, and it stores the notes in the browser's local storage so that they persist even if the user closes the application or refreshes the page.

Start by creating a new project using create-react-app:

npx create-react-app app
cd app

Now install the necessary dependencies for CKeditor 5:

npm install --save @ckeditor/ckeditor5-react @ckeditor/ckeditor5-build-classic

Remove what you have in the App.js file. Then your component will begin by importing the necessary libraries and modules, including React, the CKEditor library, and a specific version of the CKEditor called ClassicEditor. It also imports a CSS file called App.css, which is used to style the component.

import React, { Component } from 'react';
import { CKEditor } from '@ckeditor/ckeditor5-react';
import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
import './App.css';

The component's constructor method initializes the component's state with an empty array called notes to store the notes and an empty string called currentNote to store the note currently being edited.

class App extends Component {
    constructor(props) {
        this.state = {
            notes: [],
            currentNote: ""


The component's componentDidMount() method is called when the component is first rendered, and it retrieves any notes that were previously saved in local storage and sets the component's "notes" state accordingly.

componentDidMount() {
    // Retrieve notes from local storage
    let storedNotes = localStorage.getItem("notes");
    if (storedNotes) {
        this.setState({ notes: JSON.parse(storedNotes) });

The handleEditorChange method updates the component's currentNote state with the latest data from the CKEditor whenever the user makes changes to the note being edited.

handleEditorChange = (event, editor) => {
    this.setState({ currentNote: editor.getData() });

The handleSaveNote method is called when the user clicks the "Save Note" button. It adds the current note to the component's notes state and saves the updated notes to local storage. Then, it clears the currentNote state so that the user can start writing a new note.

handleSaveNote = () => {
    // Add the current note to the notes array
    let newNotes = [...this.state.notes, this.state.currentNote];
    this.setState({ notes: newNotes });

    // Save the notes array to local storage
    localStorage.setItem("notes", JSON.stringify(newNotes));

    // Clear the current note
    this.setState({ currentNote: "" });

The next step is to render the notes array in the UI. This is done in the render() method of the App component. Inside the render() method, we have a div element with the class of notes-container. Inside this div, we use the map() function to iterate through the notes array and create a new div element for each note. The key prop is set to the index of the note in the array to ensure that React can keep track of the elements.

render() {
    return (
      <div className="App">
          <h2>Note Application</h2>
              editor={ ClassicEditor }
              onChange={ this.handleEditorChange }

          <button onClick={this.handleSaveNote}>Save Note</button>
          <h3>Saved Notes:</h3>
          <div className="notes-container">
              {, index) => {
                  return <div key={index} className="note" dangerouslySetInnerHTML={{__html: note}} />

The dangerouslySetInnerHTML prop is used to render the HTML content of the note inside the div. This is a React feature that should be used with caution, as it can introduce security vulnerabilities if not used properly.

Finally, at the bottom of the code, the App component is exported so that it can be used in other parts of the application.

export default App;

Now, paste the following code in App.css. This is just basic styling for the note application.

.notes-container {
  margin: 20px 0;
  padding: 20px;
  border: 1px solid rgb(88, 80, 80);
  border-radius: 5px;

.note {
  margin: 10px 0;
  padding: 10px;
  border-radius: 5px;
  background-color: #a7a5a5;

.App {
  border: 5px solid gray;
  padding: 50px;
  border-radius: 5px;

Now, you can run your application with the following command:

npm start

complete application


This code creates a simple note-taking application that allows the user to create and save notes using the CKEditor5 rich text editor. The notes are stored in the browser's local storage so that they persist even when the user closes the browser or refreshes the page. The notes are displayed in a list in the UI and can be rendered with their original formatting.

You can learn more about CKEditor 5 by reading the official documentation and see everything it provides and its limitations.

What to do next:
  1. Try Honeybadger for FREE
    Honeybadger helps you find and fix errors before your users can even report them. Get set up in minutes and check monitoring off your to-do list.
    Start free trial
    Easy 5-minute setup — No credit card required
  2. Get the Honeybadger newsletter
    Each month we share news, best practices, and stories from the DevOps & monitoring community—exclusively for developers like you.
    author photo

    Muhammed Ali

    Muhammed is a Software Developer with a passion for technical writing and open source contribution. His areas of expertise are full-stack web development and DevOps.

    More articles by Muhammed Ali
    Stop wasting time manually checking logs for errors!

    Try the only application health monitoring tool that allows you to track application errors, uptime, and cron jobs in one simple platform.

    • Know when critical errors occur, and which customers are affected.
    • Respond instantly when your systems go down.
    • Improve the health of your systems over time.
    • Fix problems before your customers can report them!

    As developers ourselves, we hated wasting time tracking down errors—so we built the system we always wanted.

    Honeybadger tracks everything you need and nothing you don't, creating one simple solution to keep your application running and error free so you can do what you do best—release new code. Try it free and see for yourself.

    Start free trial
    Simple 5-minute setup — No credit card required

    Learn more

    "We've looked at a lot of error management systems. Honeybadger is head and shoulders above the rest and somehow gets better with every new release."
    — Michael Smith, Cofounder & CTO of YvesBlue

    Honeybadger is trusted by top companies like:

    “Everyone is in love with Honeybadger ... the UI is spot on.”
    Molly Struve, Sr. Site Reliability Engineer, Netflix
    Start free trial
    Are you using Sentry, Rollbar, Bugsnag, or Airbrake for your monitoring? Honeybadger includes error tracking with a whole suite of amazing monitoring tools — all for probably less than you're paying now. Discover why so many companies are switching to Honeybadger here.
    Start free trial