DEV Community

Sharon Rachel Levin
Sharon Rachel Levin

Posted on • Updated on

Setting up a basic rich text editor in React

As mentioned in my previous post, I've been working on a blogging app. I started writing up some posts of my own to document the experience, but quickly realized that the post content was not only plain-looking, but hard to read and unorganized.

Here's what my posts were looking like:

An image of a blog post. It contains a list of items using whitespace and asterisks in an attempt to organize the content.

Here's what I wanted them to look like!

An image of a blog post that contains a list with bulletpoints, and the headers are bigger and bolded. Certain important words and phrases are bolded/italicized.

I did a lot of research trying to find a straightforward library that would let me achieve the above. In my case, CKEditor 5 ended up being the easiest to implement in my opinion. Out of the box it is very user-friendly and has a nice, minimal design. Some features I appreciate are keyboard shortcuts (like Ctrl+B/CMD+B for bold or Ctrl+I/CMD+I for italic) and auto-formatting. Here's how I was able to set up a very basic rich text editor using CKEditor.

First up, choose your build

They have several different build options depending on what you're using it for, but in my case I used the Classic Build. Here's what the toolbar looks like:

CKEditor5's Classic Build toolbar

As you can see, it gives you the option to insert images, but you have to set up your own adapter to do so. I'll go over how to set that up in another post!

Install CKEditor and your build

Using npm:

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

Or using yarn:

yarn add @ckeditor/ckeditor5-react @ckeditor/ckeditor5-build-classic

Set up a component that will handle your form and text input

Import CKEditor and ClassicEditor (or whatever build you chose), then call on the CKEditor component inside your form. The component takes two necessary props: editor and onChange.

All you have to do is specify what build you're using in the editor prop, in this example it's editor={ClassicEditor}.

The onChange prop can be used to grab the stylized text. It uses a function that takes event and editor. We can use editor.getData() to grab that text. In my case, I'm storing it in my body state.

Note: For me, this component will be used in multiple parent components and I'm handling the form submission differently in each one, which is why I'm passing onSubmit as a prop. Your form submission might be different.
// TextEditor.js

import React, { useState } from 'react'
import CKEditor from '@ckeditor/ckeditor5-react'
import ClassicEditor from '@ckeditor/ckeditor5-build-classic'

const TextEditor = ({ onSubmit }) => {
    const [body, setBody] = useState('')

    const handleSubmit = (e) => {
        e.preventDefault()
        onSubmit({ body })
    }

    return (
        <form onSubmit={handleSubmit}>
            <CKEditor
                editor={ClassicEditor}
                onChange={(event, editor) => {
                    const data = editor.getData()
                    setBody(data)
                }}
            />
            <button type='submit'>Submit</button>
        </form>
    )
}

export default TextEditor

That's all you need to have a simple working rich text editor using React and CKEditor 5. The data I'm storing in body is in HTML, so if you want to display the output elsewhere you need to use dangerouslySetInnerHTML. Keep reading if you want to configure the toolbar plugins!

Configuring the toolbar plugins

Let's say you just want a very basic editor, and don't want to have the option to insert tables or images/media. In that case, you can set up a configuration object and pass it to your build's defaultConfig. You can also use this to separate the different sections and further personalize the toolbar to your needs.

// editorConfig.js

export const config = {
    toolbar: {
        items: [
            'bold',
            'italic',
            '|',
            'bulletedList',
            'numberedList',
            'indent',
            'outdent',
            '|',
            'heading',
            '|',
            'undo',
            'redo'
        ]
    }
}

Once the config is set up, I'm just importing it into my TextEditor file. Inside the component before the return statement, just pass the config in with ClassicEditor.defaultConfig = config.

// TextEditor.js

import React, { useState } from 'react'
import CKEditor from '@ckeditor/ckeditor5-react'
import ClassicEditor from '@ckeditor/ckeditor5-build-classic'

// import the config here
import { config } from './editorConfig'

const TextEditor = ({ onSubmit }) => {
    const [body, setBody] = useState('')

        // and then plug it in here
    ClassicEditor.defaultConfig = config

    const handleSubmit = (e) => {
        // ...
    }

    return (
        // ...
    )
}

export default TextEditor

And that's it! Here's what mine looks like in my app:

The 'Create New Post' page of my app. Shows the CKEditor toolbar and some sample text that display it's usage.

Thanks for reading 😄

Discussion (18)

Collapse
johnniepop profile image
Ivan Popov • Edited

Nice intro to CKEdit 5 for React.
I have a question though. Let's say I have an instance of this rich text editor on a page and a user tries to paste a large chunk of text (+5K lines for instance). The browser starts sweating. Well in about a minute or two it eventually pastes the text in a pretty huge, extended field. So my question is how would you prevent such situation?
One strategy would be to act early, catch the pasting event and restrict the length of the inserted text at that point. The CKEditor 5 API seem to have Clipboard and Clipboardobserver classes with the latter having a clipboardInput event but they don't seem to be easily accessible by React ... at least for me, since I'm still a noob in the front-end field.

Collapse
johnniepop profile image
Ivan Popov

At the moment I'm not sure if asked a too stupid question, or this feat is really difficult ...

Collapse
crimsonmed profile image
Médéric Burlet

The idea is indeed to restrict the length. Have you tried working with CKEdit plugins?
ckeditor.com/old/forums/CKEditor-3...

This looks promising for limiting the number of characters.

CKEdit clipboard is an extension that you also have to install:
ckeditor.com/docs/ckeditor5/latest...

You could also check this:
stackoverflow.com/questions/497090...

They look at pasting the clipboard content as text. This would remove any formatting that needs to happen and might lighten the load a lot.

Thread Thread
johnniepop profile image
Ivan Popov

Hi Médéric,
Thanks for your time.

I've seen some of the resources you provided. The point is that all of those, and the other working solutions I've found are vanilla javascript, and I can't figure how to reuse them in my react component. I mean, I already have the length altering function but I can't figure out with what prop to access it.

As for the plugin, yes it was one of the first things I tried but weirdly, after a seemingly successful installation, my project stops compiling (the '@@ckeditor/ckeditor5-clipboard' package is seen from the editor but not from the compiler).

Here (github.com/ckeditor/ckeditor5-reac...) I found a piece of useful example code but still not sure how to link it to the CKEditor component. I might ask a question directly in the project.

Cheers!

Thread Thread
johnniepop profile image
Ivan Popov
Thread Thread
johnniepop profile image
Ivan Popov • Edited

So a CKEditor dev gave some useful hints and I managed to fix my problem. I posted my solution also on stackoverflow: stackoverflow.com/questions/652636...

Collapse
dondaniel profile image
Don1011

Hello, I was really happy when I found this post. It really helped in the development of my current website. But there's an error I encountered and it was because the CKEditor element being imported from '@ckeditor/ckeditor5-react' wasn't destructured. I don't know if this is a recent development on the creator's side, but the bug was fixed after I destructured it. Please adjust the post for the sake of future readers. Thank you.

Collapse
meenachinmay profile image
Chinmay Anand

Good work, yo!!!
why don't you think for a "work together" thing if you want. i am also learning react js and posting content on this platform. i will follow you on twitter and will say Hi.

Collapse
shaerins profile image
Sharon Rachel Levin Author

thanks 😊 and sure, followed you!

Collapse
meenachinmay profile image
Chinmay Anand

Yes, texted you already in private twitter messages. waiting for reply.
🎉👏
and yeah thank you for following.

Collapse
andrewbaisden profile image
Andrew Baisden

This looks so cool.

Collapse
shaerins profile image
Sharon Rachel Levin Author

thanks 🙂

Collapse
hackhubs profile image
Abhav Thakur

Nice work

Collapse
shaerins profile image
Sharon Rachel Levin Author

thanks!

Collapse
leibnizwang profile image
Rahan Bouess

thank for this wonderful post

Collapse
lester016 profile image
Lester016

Have u tried implementing a hovering toolbar?

Collapse
shaerins profile image
Sharon Rachel Levin Author

i haven't personally , but ckeditor offers a couple of 'balloon' builds that i think can be set up similarly!

Collapse
sorooshmortazavi profile image
Soroosh Mortazavi

thank you Sharon.
is there any way to use this text editor for RTL languages?