Draft.js
Draft.js is a JavaScript framework developed by Facebook for building rich text editors within React applications. It leverages an immutable data model, specifically `immutable-js`, to manage editor state, offering functional state updates and aggressive data persistence for scalable memory usage. The latest stable version is 0.11.7, released in August 2020. However, the project has since been formally archived by Facebook on GitHub in February 2023, making it read-only and indicating that it is no longer actively maintained. This means no further bug fixes, features, or security updates are expected. Key differentiators include its tight integration with React's declarative API for rendering, selection, and input behavior, and its extensible architecture allowing for a wide variety of rich text composition experiences, from basic styling to embedded media.
Common errors
-
Cannot read properties of undefined (reading 'focus')
cause Attempting to call `editor.focus()` before the editor component's ref is properly assigned or when the component is unmounted.fixEnsure the ref (`editorRef.current` in Hooks or `this.editor` in classes) is checked for null/undefined before calling `focus()`. Use `React.useEffect` with an empty dependency array for initial focus or conditional rendering. -
The Draft.js editor is blank/empty/not visible, even though it's rendered.
cause The essential `draft-js/dist/Draft.css` stylesheet is not loaded, resulting in missing default styles for block positioning, alignment, and cursor behavior.fixAdd `import 'draft-js/dist/Draft.css';` to your application's entry file or the component where the Draft.js Editor is used. -
Editor state or selection becomes inconsistent or unresponsive after user input.
cause This often happens with asynchronous updates to React state, leading to race conditions where the editor's internal state doesn't match the DOM, or external DOM manipulation by browser extensions.fixEnsure `EditorState` updates are immediate and synchronous when passed to the `Editor` component. Avoid external DOM modifications within the `contentEditable` area. Consider using `EditorState.push` for controlled changes. -
Module not found: Error: Can't resolve 'draft-js-export-html'
cause Attempting to use utility functions (like converting `ContentState` to HTML or Markdown) from external packages without installing them.fixInstall the necessary conversion library (e.g., `draft-js-export-html`, `draft-js-import-html`) using `npm install <package-name>` or `yarn add <package-name>`.
Warnings
- breaking The Entity storage API was changed significantly between v0.10.0 and v0.11.0. While v0.10.x supported both old and new APIs, v0.11.0 removed the old API entirely. Projects upgrading from versions prior to 0.10.0 must migrate their entity handling.
- gotcha Draft.js is no longer actively maintained. The project's GitHub repository was archived in February 2023 and the last release was in August 2020. This means there will be no further bug fixes, security patches, or feature development. Consider alternatives for new projects or plan for in-house maintenance for existing ones.
- gotcha Draft.js relies heavily on `immutable-js` for its data model. This introduces an additional dependency and a functional programming paradigm that can have a steep learning curve for developers unfamiliar with immutable data structures. Performance can also be a concern with very large documents.
- gotcha The editor requires `draft-js/dist/Draft.css` to be included for basic styling. Without it, the editor might appear as a blank input field, have incorrect cursor positioning, or display blocks improperly.
- gotcha Draft.js can experience issues with delayed state updates or modifications to the DOM by browser extensions/plugins. The editor expects immediate updates and renders, and delays can cause a disconnect between keystrokes and editor state, leading to broken selection or input.
Install
-
npm install draft-js -
yarn add draft-js -
pnpm add draft-js
Imports
- Editor
const Editor = require('draft-js').Editor;import { Editor } from 'draft-js'; - EditorState
import EditorState from 'draft-js/lib/EditorState';
import { EditorState } from 'draft-js'; - Modifier
import { EditorState, Modifier } from 'draft-js/lib/DraftModifier';import { Modifier } from 'draft-js'; - RichUtils
const RichUtils = require('draft-js/lib/RichUtils');import { RichUtils } from 'draft-js';
Quickstart
import React from 'react';
import ReactDOM from 'react-dom';
import { Editor, EditorState, RichUtils } from 'draft-js';
import 'draft-js/dist/Draft.css'; // Essential for basic styling
function MyEditor() {
const [editorState, setEditorState] = React.useState(
EditorState.createEmpty()
);
const editorRef = React.useRef(null);
const focusEditor = () => {
if (editorRef.current) {
editorRef.current.focus();
}
};
React.useEffect(() => {
focusEditor();
}, []);
const handleKeyCommand = (command, editorState) => {
const newState = RichUtils.handleKeyCommand(editorState, command);
if (newState) {
setEditorState(newState);
return 'handled';
}
return 'not-handled';
};
return (
<div style={{ border: '1px solid gray', minHeight: '6em', padding: '10px' }} onClick={focusEditor}>
<Editor
ref={editorRef}
editorState={editorState}
onChange={setEditorState}
handleKeyCommand={handleKeyCommand}
placeholder="Tell a story..."
/>
</div>
);
}
const container = document.getElementById('container');
if (container) {
ReactDOM.render(<MyEditor />, container);
} else {
console.warn("Element with ID 'container' not found. Quickstart code won't render.");
}