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:
Here's what I wanted them to look like!
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:
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.
javascript
// 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.
javascript
// 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
.
javascript
// 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:
Thanks for reading 😄
Top comments (19)
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.
At the moment I'm not sure if asked a too stupid question, or this feat is really difficult ...
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.
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!
github.com/ckeditor/ckeditor5-reac...
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...
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.
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.
thanks 😊 and sure, followed you!
Yes, texted you already in private twitter messages. waiting for reply.
🎉👏
and yeah thank you for following.
This looks so cool.
thanks 🙂
Nice work
thanks!
thank for this wonderful post
Have u tried implementing a hovering toolbar?
i haven't personally , but ckeditor offers a couple of 'balloon' builds that i think can be set up similarly!
thank you Sharon.
is there any way to use this text editor for RTL languages?