React Markdown Kit editor vs MDXEditor
MDXEditor is a React component for editing Markdown and MDX,
built on Lexical and MIT licensed, where each feature is a plugin. This page compares it
with @react-markdown-kit/editor, which is also built on Lexical and edits Markdown
only.
If your content is MDX with JSX components in it, you should probably use MDXEditor. If it's Markdown and you need the files to come back unchanged, the rest of this page is for you.
Facts about MDXEditor come from its documentation and its repository. Facts about this editor link to the code or the test that backs them.
| MDXEditor | React Markdown Kit editor | |
|---|---|---|
| Content | Markdown and MDX, including JSX components | Markdown: CommonMark, plus GFM through a preset |
| Editing engine | Lexical | Lexical |
| Value in and out | Markdown string | Markdown string |
| Composition | Plugins passed in a plugins array | A preset shared with the renderer, plus extensions |
| Round trip | No published byte-identity corpus found | 22 of 22 byte-identical (test) |
| Gzipped JS for the entry | 163.1 KB (4.2.5) | 110.3 KB (0.1.0) |
| Server rendering | Client component | Client component; the renderer is a separate package that server-renders |
| Licence | MIT | MIT |
Editing model
Both editors put a Lexical surface in front of the user, and both keep the document as Markdown in your state.
MDXEditor composes features as plugins: headingsPlugin(), listsPlugin(),
tablePlugin(), jsxPlugin() and so on, passed in a plugins array. Everything is off
until you turn it on, and the toolbar is a plugin too.
<MDXEditor markdown={value} onChange={setValue} plugins={[headingsPlugin(), listsPlugin()]} />
React Markdown Kit configures the dialect instead, in a preset the renderer and the editor share, so the editor writes the same Markdown your pages read.
import { defineMarkdownPreset, gfm } from '@react-markdown-kit/renderer'
const appMarkdown = defineMarkdownPreset({ extensions: [gfm()] })
<MarkdownEditor preset={appMarkdown} value={value} onChange={setValue} />
The editing surface follows from the dialect. You don't have a list of features to keep in sync with the renderer, since both use the same preset and parser.
In practice, MDXEditor gives you finer control over which editing features exist, and this
kit makes sure preview and production render the same way, because preview uses
@react-markdown-kit/renderer instead of a second renderer.
Output format
Both hand back a Markdown string, so neither locks your content into editor JSON.
Where they differ is what counts as valid content. MDXEditor accepts MDX: JSX elements,
import and export statements, and expressions, with JSX handled by its JSX plugin
and described by a component descriptor you supply. This kit doesn't support MDX and there's no
plan to. A JSX element in the source is treated as an HTML block, kept as an opaque node and
written back as the bytes it came from.
If your documents are .mdx files with components inside them, that pretty much decides it
and MDXEditor is the right tool.
Round trip
This is the main reason the kit's editor exists.
If you open a document, switch modes and close it without typing, the file should be identical. The kit keeps the original source and the byte span of every top-level block, and writes unchanged blocks back from those bytes instead of re-serializing them.
The corpus is 22 documents that a line-oriented editor damaged on save, from
docs/AUDIT.md:
- Editor layer, bytes identical: 22 of 22, with GFM on and with GFM off
(
packages/editor/tests/roundtrip.test.ts). - Serializer layer, meaning unchanged and idempotent: 22 of 22; bytes identical
14 of 22, because that path canonicalizes on purpose
(
tests/roundtrip.test.ts).
We couldn't find an equivalent published corpus for MDXEditor, so the table says "not
found" (we're not saying it fails). The fixture is plain JSON with a cases array of { name, source, why },
and nothing in it is specific to this package, so you can run it against MDXEditor in an
afternoon and publish the result.
How to run the suite on your own documents
Bundle
Measured by scripts/compare-bundles.mjs
and stored in docs/data/bundle-sizes.json,
on 2026-09-20. Each row is the whole import closure of the recorded entry,
bundled with esbuild 0.27.7, minified, tree-shaken, with React and React DOM
external. 1 KB is 1024 bytes.
| Package | Version | Minified | Gzipped |
|---|---|---|---|
| @react-markdown-kit/editor | 0.1.0 | 349.7 KB | 110.3 KB |
| @mdxeditor/editor | 4.2.5 | 511.3 KB | 163.1 KB |
That's 52.8 KB less gzipped JavaScript for the kit's entry, with two caveats. The kit's row includes @react-markdown-kit/renderer, which is bundled in
because the editor imports it. And neither row includes CSS, since both packages ship an optional
stylesheet that the measured entry does not import, and the generator records CSS
separately when a bundle emits any.
For most applications the more relevant number isn't in this table. A page that only displays Markdown imports the renderer and never loads the editor, which is 36.8 KB gzipped for CommonMark, 48.5 KB with the GFM preset. The renderer
Server rendering
Neither rich surface server-renders. Selection, undo history and keyboard handling need
a DOM, so both components are client components, and MDXEditor's Next.js instructions
load it through dynamic() with ssr: false.
Where the two differ is what a server page has to import. In this kit the renderer and
the editor are separate packages with separate installs, so a server-rendered document
page imports @react-markdown-kit/renderer, carries no "use client" boundary, and
pulls no Lexical into the server bundle.
Server rendering
The editor also has a Node path that needs no DOM at all.
import { createMarkdownBridge } from '@react-markdown-kit/editor'
const bridge = createMarkdownBridge({ preset: appMarkdown, headless: true })
bridge.load(source)
const saved = bridge.getMarkdown()
That's the same pipeline the React editor runs, on @lexical/headless. The round-trip
tests use it, and you can use it for a migration script or a content check in CI.
Lexical versus Lexical
Both editors use the same engine, so the differences are in how each one wraps it. There are two worth knowing about.
Where Markdown is parsed. This kit parses with micromark into mdast, the same
pipeline the renderer uses, and keeps mdast as the document representation.
@lexical/markdown and its line-oriented transformer protocol are not used anywhere in
the package, because that protocol is the root cause listed in the audit.
Whether Lexical is in your types. Nothing the root entry exports requires a Lexical
type. A test asserts that src/index.ts and src/types.ts import nothing from lexical
or @lexical/*
(test). The opt-in
@react-markdown-kit/editor/lexical entry is the exception, for extension authors. The
single escape hatch, editor.getNativeEditor(), returns
unknown, so you cast it yourself and the engine doesn't end up in your types by
accident. That should let us replace the engine later without a breaking change to the
rest of the API.
How the kit wraps Lexical
Migration
The component API is small, so the swap is mostly mechanical.
// before
import { MDXEditor, headingsPlugin, listsPlugin, quotePlugin } from '@mdxeditor/editor'
import '@mdxeditor/editor/style.css'
<MDXEditor markdown={value} onChange={setValue} plugins={[headingsPlugin(), listsPlugin(), quotePlugin()]} />
// after
import { MarkdownEditor } from '@react-markdown-kit/editor'
import { defineMarkdownPreset, gfm } from '@react-markdown-kit/renderer'
import '@react-markdown-kit/editor/styles.css'
const appMarkdown = defineMarkdownPreset({ extensions: [gfm()] })
<MarkdownEditor preset={appMarkdown} value={value} onChange={setValue} />
Four things to check before you commit:
- MDX content. If any document contains a JSX element you expect to keep editing as a component, stop here. It'll be kept as an opaque block and you won't be able to edit it.
- Plugins to preset. The feature plugins have no one-to-one mapping. Decide the
dialect instead: CommonMark, or CommonMark plus
gfm(), plus any extension you render with. Presets - Toolbar. The default toolbar is on by default here.
toolbar={false}removes it, and a render prop keeps the command items while you supply the markup. Editor basics - Image uploads.
onUploadImage(file, { signal, documentKey })resolves with{ src, alt, title }, using whatever storage and endpoint you already have. Images
I'd run the round-trip script over your content directory first. It tells you in one pass whether the new dialect reads everything you already have. The script
Who should stay with MDXEditor
- Your content is MDX, or you want JSX components editable in place.
- You want to choose each editing feature independently through plugins, including turning most of them off.
- You rely on a feature this kit does not have, such as its diff and source view plugin set or its sandpack code blocks.
Who should try this one
- Your content is Markdown files or Markdown in a database, and a diff of only what the author changed matters to your team.
- You already render Markdown with React and want the editor to write exactly that dialect.
- You want the editor package out of the server bundle and out of the pages that only display content.
- You want the round trip backed by a test you can run yourself.
FAQ
Is there an alternative to MDXEditor for React?
Yes. @react-markdown-kit/editor is a rich, source and preview Markdown editor for React, also built on Lexical. It edits CommonMark and GFM (no MDX), and its tests check byte identity for 22 audited documents opened and saved without an edit.
Does React Markdown Kit edit MDX and JSX components?
No, it only edits Markdown. MDXEditor parses and edits MDX, including JSX components and imports (that's what its JSX plugin is for). If your content is MDX, I'd stay with MDXEditor.
Which editor bundle is smaller?
The measured import closure of @react-markdown-kit/editor 0.1.0 is 110.3 KB gzipped against 163.1 KB for @mdxeditor/editor 4.2.5, a difference of 52.8 KB, with React external. Both rows come from scripts/compare-bundles.mjs.
Can either editor render on the server?
Neither rich editing surface renders on a server, since both need a DOM for selection and keyboard handling. React Markdown Kit ships the renderer and editor as separate packages, so a server-rendered page that only displays Markdown imports the renderer and never loads editor code or Lexical.
Next
Try the editor demo · @react-markdown-kit/editor on npm · @mdxeditor/editor on npm · Source on GitHub
React Markdown editor · Lossless Markdown editing · Compared with Milkdown · Lexical Markdown editor guide