- Adding Editable HTML Content to a React App
- Jonathan
- What is contentEditable?
- Editable Content — Edit Me!
- What are the challenges using contentEditable with React?
- A Dangerous Solution
- How to Use react-contenteditable
- Wrap-up
- Sign up for more like this.
- React Native 0.72: What’s New and Why it Matters
- Monitor Website Content in Safari with Expedition
- Origins of Life: Building a Hypercycle Simulation with WebGL
- 5 best open-source WYSIWYG editors for React (2023)
- Draft.js
- React Quill
- Slate React
- React Draft Wysiwyg
- Official TinyMCE React component
- Conclusion
Adding Editable HTML Content to a React App
In this post, we’ll look at how contentEditable can be used with React to enable interactive, editable content in you React apps.
Jonathan
Have you ever wondered how apps like Google Docs or Notion pull off interactive, editable rich content in web apps? The usual approach to building forms with inputs like textarea can’t deliver the same kind of experience.
Instead, apps like these make use of HTML’s contentEditable attribute, which allows a user to directly edit content on a page. While enabling content editing is as simple as setting the contentEditable attribute though, actually integrating it into an app and getting usable data out of it is a different story.
In particular, if you’ve tried using contentEditable in React, you’ll quickly realize that the two don’t play nicely together–at least, not without a bit of extra work. In this post, we’ll look at how contentEditable can be used with React to enable interactive, editable content in you React apps.
What is contentEditable?
contentEditable is an attribute that can be placed on HTML elements to make the content editable by the user. We can demonstrate how contentEditable is used with the following HTML snippet:
Editable Content - Edit Me!
This piece of content has the contentEditable attribute set.
That HTML block is embedded below to show how it works in action. Go ahead, edit the content in the block below!
Editable Content — Edit Me!
This piece of content has the contentEditable attribute set.
As mentioned earlier, enabling editing with contentEditable is simple–but it’s not always easy to make it work with your app. To fully build an app with contentEditable , you need to address the following issues in one way or another.
- Add change handlers for the updated content
- Add validation and sanitization of the edited content
- Add translation to-and-from your internal data representation (most apps do not store raw HTML for the content)
- Make content editable play nicely with the rest of your app’s data lifecycle
This last point is a particular challenge with React– contentEditable and React conflict in how they manage and update DOM state.
What are the challenges using contentEditable with React?
Before looking at contentEditable with React, let’s quickly look at how we might implement non-rich text editing with textarea :
const TextAreaEditable = () => < const [content, setContent] = React.useState("") return ( onChange= setContent(e.currentTarget.value)> /> ) >
But if we try to follow this model for contentEditable content in a React app, we’ll quickly start running into errors, warnings and unexpected behaviors. Let’s take a look at a naive attempt at adapting the code above for contentEditable :
🚫 Our naive attempt at using contentEditable: const Editable = () => < const [content, setContent] = React.useState("") return ( setContent(t.currentTarget.innerHTML)> contentEditable> ) >
If we look in the console, we see this warning as soon as we render the page:
Warning: A component is contentEditable and contains children managed by React. It is now your responsibility to guarantee that none of those nodes are unexpectedly modified or duplicated. This is probably not intentional.
The problem is that with contentEditable enabled, both React and the browser want to handle the state of the content inside of the element. Additionally, as we start to edit the content, we’ll run into some other problems.
If I start editing the content, and hit command-B to bold some of the text I’m editing, I’ll notice two things:
First of all–it works!–without doing any special handling, we’re able to do the kind of text style manipulation we’d expect from an interactive editor.
But the second thing I noticie is that when the view blurs and the state update occurs, my text gets transformed into a mess of HTML! Instead of seeing the text «123″, I see 123
The issue, of course, is that our content string is pure HTML, but we’re trying to render it in React by passing it as a child of div . React does not interpret those children as HTML and escapes the content instead.
A Dangerous Solution
To make our contentEditable component work correctly, we need to tell React to render an HTML string, instead of plain text. Fortunately, such a technique exists, but it has a scary sounding name: dangerouslySetInnerHTML .
The dangerouslySetInnerHTML attribute can be used to set the inner HTML of an element, letting us rewrite our Editable component:
✅ Our second attempt at using contentEditable: const Editable = () => < const [content, setContent] = React.useState("") const onContentBlur = React.useCallback(evt =>setContent(evt.currentTarget.innerHTML)) return ( dangerouslySetInnerHTML= /> ) >
As the name suggests, dangerouslySetInnerHTML is dangerous. Why? Because unsanitized HTML input can be a security risk and expose users to cross-site scripting (XSS) attacks. Imagine we’re using contentEditable to enable collaboration between users. If we’re not careful, an attacker could embed JavaScript in the content that will get executed in the victim’s browser when dangerouslySetInnerHTML is used.
Because of this, it’s always recommended to use some form of input sanitization when using contentEditable and dangerouslySetInnerHTML . The package sanitize-html can help enforce a set of whitelisted HTML tags to prevent XSS attacks.
✅ Sanitized HTML with contentEditable: import sanitizeHtml from "sanitize-html" const Editable = () => < const [content, setContent] = React.useState("") const onContentBlur = React.useCallback(evt => < const sanitizeConf = < allowedTags: ["b", "i", "a", "p"], allowedAttributes: < a: ["href"] >>; setContent(sanitizeHtml(evt.currentTarget.innerHTML, sanitizeConf)) >, []) return ( dangerouslySetInnerHTML=> /> ) >
Note that to be sure your application isn’t subject to XSS attacks, it’s not enough to only validate during editing. You also need to sanitize and validate content on the server-side or on the content coming back from the server! If you’re only sanitizing when a user edits the content, a clever attacker can bypass the safety checks and send unsafe data directly to the server.
How to Use react-contenteditable
While the basic approach above lets you use contentEditable in your React app, you may notice some edge cases as you start to implement features in your app. Specifically, if you add additional event handlers to your div to capture input before the blur event, you might see cursor jumping and other undesirable behaviors. That’s because a bit of additional logic is needed to determine when to actually modify the content state and trigger a re-render.
Though we could add a workaround for this and other edge cases in our component without too much trouble, why re-invent the wheel? The react-contenteditable package solves most of the common issues you’re likely to encounter with contentEditable in a React app.
Using this package is a pretty simple change from our previous example:
✅ Sanitized HTML with the ContentEditable package: import sanitizeHtml from "sanitize-html" import ContentEditable from 'react-contenteditable'; const Editable = () => < const [content, setContent] = React.useState("") const onContentChange = React.useCallback(evt => < const sanitizeConf = < allowedTags: ["b", "i", "a", "p"], allowedAttributes: < a: ["href"] >>; setContent(sanitizeHtml(evt.currentTarget.innerHTML, sanitizeConf)) >, []) return ( onBlur= html= /> ) >
By using sanitize-html and react-contenteditable , we can quickly build the basics of rich, interactive content editing in a React app.
Wrap-up
In this post, we’ve learned a bit about how to add useer editable content to your React app. The main considerations are:
- contentEditable in React requires some special handling.
- The react-contenteditable package can help handle edge cases you might encounter.
- Remember to sanitize HTML both when editing and before rendering untrusted content. The sanitize-html package can help with this.
Using this simple approach, you can start adding editable content to any React app!
Sign up for more like this.
React Native 0.72: What’s New and Why it Matters
New features in React Native bring a ton of important improvements. In this post, we’ll see what’s new in React Native 0.72.
Monitor Website Content in Safari with Expedition
Expedition is a simple Safari extension to monitor prices, social media stats, sporting events and more.
Origins of Life: Building a Hypercycle Simulation with WebGL
Though the exact origins of life on earth remain a mystery, the hypercycle helps explain the emergence of self-replication. In this post we’ll learn about hypercycles and how to build a simulation using WebGL.
5 best open-source WYSIWYG editors for React (2023)
WYSIWYG (What You See Is What You Get or Rich Text) editors are commonly used on many websites and web apps related to newspapers, blogs, e-commerce, forums, and online communities to help users publish and edit articles, product information, directions manual, etc. with functions such as alignment, change font size and text color, insert images, and so on.
This article walks you through a list of the best and most popular open-source WYSIWYG editors for React. Their order is based on the number of stars they receive on Github and the number of downloads per week on npmjs.com.
Draft.js
- GitHub stars: 22.3k+
- Weekly NPM downloads: 800k -1m
- Links: GitHub repo | npm page | live demo | official website
- License: MIT
- Written in: Javascript, CSS
Draft.js is a highly extensible and customizable JavaScript rich-text editor framework built for React and fits seamlessly into React applications, abstracting away the details of rendering, selection, and input behavior with a familiar declarative API.
import React from 'react'; import ReactDOM from 'react-dom'; import from 'draft-js'; const MyEditor = props => < const [editorState, setEditorState] = React.useState( EditorState.createEmpty() ); const editor = React.useRef(null); function focusEditor() < editor.current.focus(); >React.useEffect(() => < focusEditor() >, []); return ( > editorState= onChange= setEditorState(editorState)> /> ); >
React Quill
- GitHub stars: 5.5k+
- Weekly NPM downloads: 400k – 450k
- Links: GitHub repo | npm page | live demo
- License: MIT
- Written in: Javascript, TypeScript
React Quill is a rich text editor that is fully compatible with React hooks and TypeScript. It provides 3 built-in themes:
- Snow: A Quill’s standard appearance,
- Bubble: Similar to the inline editor on Medium
- Core theme: Contains only the bare essentials to allow modules like toolbars or tooltips to work.
import React, < useState >from "react"; import ReactQuill from 'react-quill'; import 'react-quill/dist/quill.snow.css'; const MyEditor = () => < const [value, setValue] = useState(''); return ( onChange=/> ); >
Slate React
- GitHub stars: 26.2k+
- Weekly NPM downloads: 350k – 450k
- Links: GitHub repo | npm page | live demo | official website
- License: MIT
- Written in: Javascript, TypeScript, CSS
Slate lets you build rich, intuitive editors like those in Medium, Dropbox Paper, or Google Docs without your codebase getting mired in complexity. t can do this because all of its logic is implemented with a series of plugins, so you aren’t ever constrained by what is or isn’t in “core”.
React Draft Wysiwyg
- GitHub stars: 5.9k+
- Weekly NPM downloads: 180k -250k
- Links: GitHub repo | npm page | live demo
- License: MIT
- Written in: Javascript, CSS
This is a lightweight rich text editor built on top of ReactJS and DraftJS. It provides lots of features:
- Configurable toolbar with the option to add/remove controls.
- Option to change the order of the controls in the toolbar.
- Support for block types: Paragraph, H1 – H6, Blockquote, Code.
- Support for coloring text or background.
- Support for adding/uploading images.
- Option of undo and redo.
Official TinyMCE React component
- GitHub stars: 750+
- Weekly NPM downloads: 150k – 200k
- Links: GitHub repo | npm page | live demo | official website
- License: Apache License, Version 2.0
- Written in: Almost TypeScript
TinyMCE is an advanced open-source core-rich text editor that can be easily integrated into your React projects.
It is easy to configure the UI to match the layout design of your site, product, or application. Due to its flexibility, you can configure the editor with as much or as little functionality as you like, depending on your requirements.
With a ton of powerful plugins available, extending and adding additional functionality to TinyMCE is as simple as including a single line of code.
import React, < useRef >from 'react'; import < Editor >from '@tinymce/tinymce-react'; const App = () => < const editorRef = useRef(null); const log = () => < if (editorRef.current) < console.log(editorRef.current.getContent()); >>; return ( <> editorRef.current = editor> initialValue="This is the initial content of the editor.
" init=' >> /> >); > export default App;
Conclusion
We’ve gone over a collection of great, free, and open-source rich text editors that you can use in your React applications. Which one will you choose? Please let us know in the comment section.
If you’d like to explore more new and exciting things in React and front-end development, take a look at the following articles:
You can also check our React topic page and React Native topic page for the latest tutorials and examples.