Draft.js Plugins Editor
The pluggable React editor component at the core of Draft.js Plugins, now archived following Draft.js's 2023 sunset.
Repository Health
Technical Analysis
@draft-js-plugins/editor is the core <Editor> component of the Draft.js Plugins monorepo — a drop-in replacement for Facebook’s own draft-js <Editor> that accepts a plugins array instead of a single fixed set of handlers and decorators. Each plugin is just a plain object implementing any subset of draft-js’s own hook names (onChange, handleReturn, blockRendererFn, keyBindingFn, decorators, etc.), and the editor’s job is purely to fan those hooks out and merge them back into one Editor instance — no separate plugin API to learn beyond draft-js itself.
Around this editor, the same monorepo publishes two dozen sibling plugin packages (mention, hashtag, emoji, linkify, image, alignment, and more) that all implement this same EditorPlugin interface, which is why the editor package exists as its own dependency rather than being bundled into any one plugin.
The project carries an important caveat: both the monorepo’s README and its GitHub repository mark it as deprecated and archived. Facebook stopped maintaining the underlying draft-js editor in 2023, and Draft.js Plugins followed suit — no further bug fixes or features are planned. Existing installs continue to work, but teams starting new rich-text editor projects should weigh actively maintained alternatives (Lexical, ProseMirror/Tiptap, Slate) before adopting it.
What You Get
- A
PluginEditorcomponent that’s API-compatible with draft-js’s own<Editor>, so existing draft-js knowledge and props transfer directly - A
pluginsprop that accepts an array of plain objects — no plugin registration API, no build step, just object literals implementing draft-js hook names - Automatic fan-out of same-named hooks across multiple plugins (
createPluginHooks), so several plugins’onChange,handleReturn, orblockRendererFncan coexist on one editor - A
MultiDecoratorthat merges each plugin’s own decorators (and any custom ones passed directly) into a single draft-js-compatible decorator createEditorStateWithTextandcomposeDecoratorshelper exports for building initial editor state and combining custom decorators manually- TypeScript type definitions for the
EditorPlugininterface, so authoring a new plugin gets full editor autocomplete
Common Use Cases
- Building a rich-text editor that needs Slack-style emoji, Facebook-style @mentions, and #hashtags without hand-wiring three separate decorator/handler systems
- Composing a CMS or comment-box editor from several of the sibling draft-js-plugins packages (mention, linkify, image, inline-toolbar) at once
- Maintaining an existing product already built on Draft.js Plugins that can’t yet migrate to a newer rich-text stack
- Writing a custom in-house plugin (autolist, single-line, markdown-shortcuts style) against a stable, documented
EditorPlugininterface
Under The Hood
Architecture
The package exports a single React class component, PluginEditor (src/Editor/index.tsx), wrapping draft-js’s own Editor, alongside a small set of pure-function utilities (resolveDecorators.ts, createCompositeDecorator.tsx, MultiDecorator.ts, PluginHooks.ts, defaultKeyBindings.ts, defaultKeyCommands.ts). On construction and on every prop update it walks an array of plugin objects — each a plain object of optional draft-js callback hooks (onChange, keyBindingFn, handleReturn, blockRendererFn, and more) — and merges their hooks via createPluginHooks, which fans them out onto Editor props by naming convention (on* event hooks, handle* handler hooks, *Fn function hooks). Decorators from every plugin are merged through a custom MultiDecorator that concatenates per-plugin CompositeDecorators into one draft-js-compatible decorator. Data flow stays unidirectional: editorState and onChange pass straight through from the host app, intercepted only so each plugin’s own onChange can transform state before it’s forwarded downstream. Because every sibling plugin package in the monorepo implements the same EditorPlugin interface, a breaking change to that interface would ripple across all of them at once.
Tech Stack
Written in strict-mode TypeScript targeting ESNext, built with Rollup (shared rollup.config.js at the repo root) into dual ESM/CJS bundles plus .d.ts declarations via tsc -d. Runtime dependencies are minimal — immutable (pinned to the same major draft-js itself uses) backs the List/Map structures behind decorators, and prop-types provides legacy runtime prop validation alongside the TypeScript types. draft-js, react, and react-dom are peer dependencies with open ranges from React 16.8 through 18+, so the actual editing engine is bring-your-own. Tests run on Jest with Babel transforms; the monorepo itself is orchestrated with Yarn workspaces and Changesets for versioning, with CI on GitHub Actions.
Code Quality
A real Jest configuration exists (jest.config.js, babel.config.js), and other plugin packages in the monorepo do run executing tests, but this editor package’s own suite is disabled: index.test.ts is a single test.skip wrapping a large commented-out Enzyme/Chai/Sinon test suite, with a comment noting the tests were removed pending a rewrite to React hooks — so the core PluginEditor component currently ships with no executing tests of its own. TypeScript strict mode is enabled and used consistently (no untyped any outside one explicitly ts-ignored spread), naming follows conventional camelCase/PascalCase, and ESLint, Prettier, and Husky/lint-staged run at the repo root with CI executing lint, build, and test on every PR.
API Design
The public surface is intentionally small: a drop-in replacement for draft-js’s own <Editor> that accepts a plugins array of plain objects implementing any subset of draft-js’s existing hook names, so prior draft-js knowledge transfers directly and a “plugin” requires no special registration step, just an object literal. The composition mechanism — createPluginHooks fanning multiple plugins’ same-named hooks onto one Editor prop, and MultiDecorator merging multiple plugins’ decorators into one — is the package’s one genuinely well-executed piece of engineering, more a polished adapter pattern than a new capability. Documentation is strong at the ecosystem level (a dedicated docs site with a page per plugin, plus a HOW_TO_CREATE_A_PLUGIN.md guide), though this specific package’s own README is just a one-line pointer back to the monorepo README.