はじめに
「アプリにMarkdownエディタを組み込みたいけれど、テキストエリアとプレビューを並べただけの画面では使いにくい」——そんな悩みを抱えたことはありませんか?
エンジニアにとってMarkdownは慣れ親しんだ記法ですが、非エンジニアのユーザーにとっては「# や ** って何?」という世界です。かといって、リッチテキストエディタを自前で実装するのは膨大な工数がかかります。
そんな課題を解決してくれるのが、オープンソースのReactコンポーネント「MDXEditor」です。Google DocsやNotionのような直感的な編集体験を提供しながら、データはあくまでMarkdownとして保存できる、いいとこ取りのライブラリです。この記事では、MDXEditorの基本から実践的な使い方まで、コピペで動くコード例とともに解説します。
説明を読むより先に、実際にMDXEditorを触ってみた方が早いと思います。ブラウザ上で編集できるサンプルを用意したので、先に挙動を確認したい方はこちらからどうぞ。
MDXEditorとは
MDXEditorは、Markdownドキュメントを「見たまま」編集できるReact製のWYSIWYGエディタコンポーネントです。WYSIWYG(What You See Is What You Get)とは、編集画面の見た目がそのまま最終的な表示になる方式のことで、プレビュー画面との切り替えが不要になります。
MITライセンスで公開されており、GitHubで3.6k以上のスターを獲得している人気プロジェクトです。2026年6月にはメジャーバージョンとなるv4系がリリースされ、活発に開発が続いています。
主な特徴
- 真のWYSIWYG編集 - 「編集画面」と「プレビュー画面」の分離がなく、Google DocsやNotionのように書いたそばから整形されます。非エンジニアでも迷わず使えます
- 出力はプレーンなMarkdown - エディタ上ではリッチに見えても、保存されるデータは標準的なMarkdown文字列です。既存のMarkdownベースのシステムにそのまま組み込めます
- プラグインアーキテクチャ - 見出し、リスト、テーブル、画像、コードブロックなどの機能がすべてプラグインとして分離されています。必要な機能だけを選んで軽量に保てます
- コードブロックの本格編集 - CodeMirrorを統合した編集可能なコードブロックに対応し、言語ごとのシンタックスハイライトも利用できます
- MDX(JSXコンポーネント)対応 - 名前の由来でもあるMDXをサポートし、Markdown中のReactコンポーネントも編集できます
インストール
npmやyarn、pnpmでインストールできます。
# npm
npm install --save @mdxeditor/editor
# yarn
yarn add @mdxeditor/editor
# pnpm
pnpm add @mdxeditor/editor
MDXEditorのサンプルを動かす
実際に手を動かしてMDXEditorのふるまいを確認してみましょう。下のエディタはheadingsPlugin・listsPlugin・quotePluginに加えて、toolbarPluginでUndoRedo・BoldItalicUnderlineToggles・BlockTypeSelectの3つのツールバーボタンを表示しています。テキストを選択して太字にしたり見出しに変換したりすると、onChangeで受け取ったMarkdown文字列がそのままエディタの下に表示される仕組みです。
要点だけを抜き出すと、こういう構成です。
import { useState } from 'react'
import {
MDXEditor,
headingsPlugin,
toolbarPlugin,
UndoRedo,
BoldItalicUnderlineToggles,
} from '@mdxeditor/editor'
import '@mdxeditor/editor/style.css'
export default function LiveDemo() {
const [markdown, setMarkdown] = useState('# Hello, MDXEditor')
return (
<MDXEditor
markdown={markdown}
onChange={setMarkdown}
plugins={[
headingsPlugin(),
toolbarPlugin({
toolbarContents: () => (
<>
<UndoRedo />
<BoldItalicUnderlineToggles />
</>
),
}),
]}
/>
)
}
実際に動かせるものが下です。エディタ内のテキストを編集したり、ツールバーのボタンを押したりしてみてください。
エディタ内のテキストを選択して太字ボタンを押すと、下の<pre>に表示されているMarkdown文字列にも**が反映されるのが分かります。toolbarContentsにCreateLinkやInsertTableのような他のコンポーネントを追加すれば、そのままツールバーのボタンを増やせます。plugins配列からquotePlugin()を外すと、引用ブロックへの変換ボタンが効かなくなる点も試して確認してみてください。
基本的な使い方
最小構成のエディタは、わずか数行で動きます。付属のCSSファイルのインポートを忘れないようにしましょう。
import { MDXEditor, headingsPlugin } from '@mdxeditor/editor'
import '@mdxeditor/editor/style.css'
export default function App() {
return <MDXEditor markdown="# Hello World" plugins={[headingsPlugin()]} />
}
これだけで、見出しをWYSIWYGで編集できるエディタが表示されます。
プラグインで機能を追加する
MDXEditorの真価はプラグインシステムにあります。よく使う機能をまとめて有効化してみましょう。
import {
MDXEditor,
headingsPlugin,
listsPlugin,
quotePlugin,
thematicBreakPlugin,
markdownShortcutPlugin,
} from '@mdxeditor/editor'
import '@mdxeditor/editor/style.css'
export default function Editor() {
return (
<MDXEditor
markdown="# ようこそ MDXEditor へ"
plugins={[
headingsPlugin(), // 見出し
listsPlugin(), // 箇条書き・番号付きリスト
quotePlugin(), // 引用
thematicBreakPlugin(), // 水平線
markdownShortcutPlugin() // 「# + スペース」などのMarkdownショートカット
]}
/>
)
}
markdownShortcutPlugin を入れると、# に続けてスペースを打つと見出しに変換されるなど、Markdownに慣れたユーザーの入力体験も損ないません。
ツールバーを表示する
非エンジニア向けには、ボタン操作できるツールバーが欠かせません。toolbarPlugin に表示したいコンポーネントを渡します。
import {
MDXEditor,
headingsPlugin,
listsPlugin,
quotePlugin,
toolbarPlugin,
UndoRedo,
BoldItalicUnderlineToggles,
BlockTypeSelect,
} from '@mdxeditor/editor'
import '@mdxeditor/editor/style.css'
export default function EditorWithToolbar() {
return (
<MDXEditor
markdown="ツールバー付きエディタです"
plugins={[
headingsPlugin(),
listsPlugin(),
quotePlugin(),
toolbarPlugin({
toolbarContents: () => (
<>
<UndoRedo />
<BoldItalicUnderlineToggles />
<BlockTypeSelect />
</>
),
}),
]}
/>
)
}
ツールバーの中身はReactコンポーネントとして自由に組み替えられるため、アプリの要件に合わせて必要なボタンだけを並べられます。
編集内容を取得する
フォームとして使う場合は、onChange で最新のMarkdown文字列を受け取れます。
import { useState } from 'react'
import { MDXEditor, headingsPlugin, listsPlugin } from '@mdxeditor/editor'
import '@mdxeditor/editor/style.css'
export default function EditorForm() {
const [markdown, setMarkdown] = useState('# 下書き')
return (
<div>
<MDXEditor
markdown={markdown}
onChange={setMarkdown}
plugins={[headingsPlugin(), listsPlugin()]}
/>
<p>現在の文字数: {markdown.length}</p>
</div>
)
}
状態として保持したMarkdownをそのままAPIに送信すれば、保存処理も簡単に実装できます。
実践的なユースケース
Next.js(App Router)に組み込む
MDXEditorはブラウザのAPIに依存しているため、サーバーサイドレンダリング(SSR)には対応していません。Next.jsで使う場合は next/dynamic を使い、クライアント側でのみ読み込むようにします。
まず、エディタ本体をクライアントコンポーネントとして切り出します。
// components/InitializedMDXEditor.tsx
'use client'
import {
MDXEditor,
headingsPlugin,
listsPlugin,
quotePlugin,
markdownShortcutPlugin,
type MDXEditorProps,
} from '@mdxeditor/editor'
import '@mdxeditor/editor/style.css'
export default function InitializedMDXEditor(props: MDXEditorProps) {
return (
<MDXEditor
plugins={[
headingsPlugin(),
listsPlugin(),
quotePlugin(),
markdownShortcutPlugin(),
]}
{...props}
/>
)
}
次に、dynamic で SSR を無効化して読み込みます。
// app/edit/page.tsx
'use client'
import dynamic from 'next/dynamic'
const Editor = dynamic(() => import('@/components/InitializedMDXEditor'), {
ssr: false,
loading: () => <p>エディタを読み込み中...</p>,
})
export default function EditPage() {
return <Editor markdown="# Next.jsで動くMDXEditor" />
}
公式が Next.js用のサンプルリポジトリ を公開しているので、詰まったときはそちらも参考になります。
ブログのCMS風エディタを作る
コードブロックや画像、テーブルまで扱えるフル機能のエディタも、プラグインを足すだけで構築できます。
import {
MDXEditor,
headingsPlugin,
listsPlugin,
quotePlugin,
linkPlugin,
linkDialogPlugin,
imagePlugin,
tablePlugin,
codeBlockPlugin,
codeMirrorPlugin,
markdownShortcutPlugin,
} from '@mdxeditor/editor'
import '@mdxeditor/editor/style.css'
export default function BlogEditor({
initialContent,
onSave,
}: {
initialContent: string
onSave: (markdown: string) => void
}) {
return (
<MDXEditor
markdown={initialContent}
onChange={onSave}
contentEditableClassName="prose max-w-none"
plugins={[
headingsPlugin(),
listsPlugin(),
quotePlugin(),
linkPlugin(),
linkDialogPlugin(), // リンク挿入ダイアログ
imagePlugin(), // 画像の挿入
tablePlugin(), // テーブルのGUI編集
codeBlockPlugin({ defaultCodeBlockLanguage: 'ts' }),
codeMirrorPlugin({
codeBlockLanguages: { ts: 'TypeScript', js: 'JavaScript', css: 'CSS' },
}),
markdownShortcutPlugin(),
]}
/>
)
}
contentEditableClassName に任意のCSSクラスを指定できるため、Tailwind CSSの prose クラス(@tailwindcss/typography)を当てれば、公開ページと同じ見た目で編集できます。テーブルはセル単位でGUI編集でき、コードブロック内ではCodeMirrorによる言語別ハイライトが効きます。
このように「編集体験はNotion風、保存データはMarkdown」という構成にしておけば、既存の静的サイトジェネレータやMarkdownベースのブログシステムと自然に連携できます。
markdownShortcutPluginでMarkdown記法をショートカット入力する
headingsPluginやlistsPlugin、quotePluginを有効にしただけでは、見出しやリストへの変換にツールバー操作が必要です。ここにmarkdownShortcutPluginを追加すると、# や- 、> のようなMarkdown記法をそのままタイプするだけでリッチテキストに変換されるようになります。
import { MDXEditor, headingsPlugin, listsPlugin, quotePlugin, markdownShortcutPlugin } from '@mdxeditor/editor'
import '@mdxeditor/editor/style.css'
export default function ShortcutDemo() {
return (
<MDXEditor
markdown="ここに `# ` や `- ` と入力してみてください"
plugins={[
headingsPlugin(),
listsPlugin(),
quotePlugin(),
markdownShortcutPlugin(), // Markdown記法での入力を有効化
]}
/>
)
}
行頭で## と入力するとH2見出しに、- だと箇条書きリストに変わります。下の<pre>にはその都度onChangeで受け取ったMarkdown文字列が表示されるので、見た目上はリッチテキストでも保存されるデータはあくまで##や-から始まるプレーンなMarkdownだと確認できます。markdownShortcutPluginを配列から外すと、この自動変換が効かなくなる点も試してみてください。
tablePluginでテーブルをGUI編集する
コードブロックや画像だけでなく、tablePluginを有効にすればMarkdownのテーブルもGUIで編集できます。セルにカーソルを合わせると、行や列の追加・削除を行うハンドルが表示される仕組みです。
import { MDXEditor, tablePlugin } from '@mdxeditor/editor'
import '@mdxeditor/editor/style.css'
const markdown = `
| 項目 | 値 |
| --- | --- |
| 名前 | MDXEditor |
| 種別 | React製エディタ |
`
export default function TableDemo() {
return <MDXEditor markdown={markdown} plugins={[tablePlugin()]} />
}
セルをクリックしてから行末・列末に出るボタンを押すと、行や列を追加できます。追加や削除をするたびに、下の<pre>のMarkdownテーブル記法(| --- | --- |の区切り行込み)が自動で更新されるのが分かります。tablePluginは単体でも動きますが、実際のブログ入稿画面ではtoolbarPluginと組み合わせてInsertTableボタンから新規テーブルを追加できるようにするのが実用的です。
readOnlyプロパティで表示専用にする
コメントの引用表示や、確定済みドキュメントの閲覧画面など、編集させたくない場面もあります。そんなときはMDXEditorのreadOnlyプロパティにtrueを渡すだけで、ツールバー操作も含めて編集を無効化できます。
import { useState } from 'react'
import { MDXEditor, headingsPlugin } from '@mdxeditor/editor'
import '@mdxeditor/editor/style.css'
export default function ReadOnlyDemo() {
const [readOnly, setReadOnly] = useState(false)
return (
<>
<label>
<input
type="checkbox"
checked={readOnly}
onChange={(e) => setReadOnly(e.target.checked)}
/>
読み取り専用にする
</label>
<MDXEditor
markdown="# 編集できるかどうか試してみてください"
readOnly={readOnly}
plugins={[headingsPlugin()]}
/>
</>
)
}
チェックボックスを入れるとreadOnlyがtrueになり、テキストの選択はできてもキー入力やツールバー操作が一切効かなくなるのが分かります。公式ドキュメントでも、readOnlyは編集操作を止める用途向けで、Markdownをそのまま静的に表示する用途には推奨されていません。本当に「見た目だけ編集画面のまま固定したい」場面に絞って使うのがおすすめです。
まとめ
この記事では、React製WYSIWYG MarkdownエディタのMDXEditorについて解説しました。
- MDXEditorは編集画面とプレビューの分離をなくす、真のWYSIWYG体験を提供します
- 出力はプレーンなMarkdownなので、既存システムへの組み込みが容易です
- プラグインアーキテクチャにより、必要な機能だけを選んで軽量に構成できます
- SSRには非対応のため、Next.jsでは
dynamicのssr: falseで読み込みます
社内ツールのドキュメント編集、CMSの記事入稿画面、コメント欄のリッチ化など、「Markdownで保存したいけれど編集は快適にしたい」場面で幅広く活躍します。まずは基本プラグインだけの最小構成から試して、必要に応じてツールバーやテーブル編集を足していくのがおすすめです。公式サイトのライブデモでは全機能をブラウザ上で試せるので、ぜひ触ってみてください。