はじめに
「アプリにMarkdownエディタを組み込みたい」と思ったとき、どんな選択肢が思い浮かぶでしょうか。テキストエリアとプレビューを左右に並べる古典的な2ペイン方式は実装が簡単ですが、いまどきのユーザーはNotionやTyporaのような「書いたそばからリッチに表示される」WYSIWYG体験に慣れています。かといって、ProseMirrorを素で触ってWYSIWYGエディタを自作するのは、正直かなり骨の折れる仕事です。
そんなジレンマを解決してくれるのが、今回紹介するMilkdownです。ProseMirrorとRemarkという実績あるライブラリの上に構築されたプラグイン駆動のフレームワークで、付属のCrepeエディタを使えば、NotionライクなWYSIWYG Markdownエディタがわずか数行のコードで手に入ります。
Milkdownとは
Milkdownは、Typoraにインスピレーションを受けて開発された、WYSIWYG Markdownエディタを構築するためのプラグイン駆動フレームワークです。エディタの実体はProseMirror(リッチテキスト編集エンジン)、Markdownの解析・生成はRemarkが担っており、その2つを橋渡しする形で設計されています。
GitHubのスター数は11.7kを超え、2021年の公開以来アクティブに開発が続いています。本記事執筆時点の最新バージョンはv7.21.3です(2026年7月時点)。
主な特徴
- プラグイン駆動 - 構文、テーマ、UIコンポーネントまで、すべてがプラグインとして提供されます。必要な機能だけを選んで組み合わせられるため、バンドルサイズを最小限に抑えられます
- すぐ使える完成品エディタ「Crepe」 - フレームワークを一から組み立てなくても、
@milkdown/crepeパッケージを使えばスラッシュコマンドやツールバー付きのNotionライクなエディタが即座に動きます - ヘッドレス設計 - コア部分にはCSSが一切含まれていません。アプリのデザインシステムに合わせて、見た目を完全にコントロールできます
- リアルタイム共同編集対応 - Y.jsとの連携プラグインが公式に用意されており、Google Docsのような同時編集機能を組み込めます
- 信頼できる技術基盤 - ProseMirror・Remark・Y.jsという、それぞれの分野で実績のあるライブラリの上に構築されています
インストール
まずは手軽に始められるCrepeエディタをインストールしてみましょう。
# npm
npm install @milkdown/crepe
# pnpm
pnpm add @milkdown/crepe
# yarn
yarn add @milkdown/crepe
機能を自分で選んで組み立てたい場合は、コアと公式プラグイン群をまとめた@milkdown/kitを使います。
npm install @milkdown/kit
基本的な使い方
Crepeで最速セットアップ
Crepeを使えば、以下のコードだけでWYSIWYGエディタが立ち上がります。
import { Crepe } from '@milkdown/crepe'
import '@milkdown/crepe/theme/common/style.css'
import '@milkdown/crepe/theme/frame.css'
const crepe = new Crepe({
root: '#app',
defaultValue: '# Hello, Milkdown!',
})
crepe.create()
たったこれだけで、見出しやリストがその場でレンダリングされ、/を入力するとブロック挿入メニューが開く、Notionライクなエディタが完成します。テーマCSSはframe.cssのほかにも複数用意されており、差し替えるだけで雰囲気を変えられます。
機能の取捨選択
Crepeは内部機能をフラグで細かくオン・オフできます。たとえば「ツールバーは不要、コードブロックのシンタックスハイライトは欲しい」といった調整も簡単です。
import { Crepe } from '@milkdown/crepe'
const crepe = new Crepe({
root: '#app',
features: {
[Crepe.Feature.Toolbar]: false, // 選択時のフローティングツールバーを無効化
[Crepe.Feature.Latex]: true, // LaTeX数式のサポートを有効化
},
})
crepe.create()
@milkdown/kitでゼロから組み立てる
より細かく制御したい場合は、@milkdown/kitでエディタを構成します。プラグインを.use()で積み上げていくスタイルです。
import { Editor } from '@milkdown/kit/core'
import { commonmark } from '@milkdown/kit/preset/commonmark'
import { history } from '@milkdown/kit/plugin/history'
import { nord } from '@milkdown/theme-nord'
import '@milkdown/theme-nord/style.css'
const editor = await Editor.make()
.config(nord) // テーマの適用
.use(commonmark) // CommonMark構文のサポート
.use(history) // Undo / Redo
.create()
「CommonMarkだけで十分」「GFMのテーブルも欲しい」といった要件に応じて、プリセットやプラグインを足し引きするだけで構成が変わります。この見通しの良さがプラグイン駆動設計の魅力です。
Reactと組み合わせる
実際のプロジェクトではReactなどのフレームワークと組み合わせることが多いはずです。公式パッケージ@milkdown/reactが用意されており、Vue向けの@milkdown/vueもあります。
npm install @milkdown/crepe @milkdown/react @milkdown/kit
import { Crepe } from '@milkdown/crepe'
import { Milkdown, MilkdownProvider, useEditor } from '@milkdown/react'
import '@milkdown/crepe/theme/common/style.css'
import '@milkdown/crepe/theme/frame.css'
const CrepeEditor: React.FC = () => {
useEditor((root) => new Crepe({ root }))
return <Milkdown />
}
export const MilkdownEditorWrapper: React.FC = () => {
return (
<MilkdownProvider>
<CrepeEditor />
</MilkdownProvider>
)
}
useEditorフックにエディタの生成関数を渡し、<Milkdown />コンポーネントを描画するだけです。エディタのライフサイクル管理はライブラリ側が面倒を見てくれます。
実践的なユースケース
ブログCMSの入稿画面を作る
エディタを組み込むアプリで必ず必要になるのが「編集内容の取得と保存」です。CrepeにはMarkdown文字列を取り出すgetMarkdown()と、変更を監視するリスナーが用意されています。
import { Crepe } from '@milkdown/crepe'
import '@milkdown/crepe/theme/common/style.css'
import '@milkdown/crepe/theme/frame.css'
const crepe = new Crepe({
root: '#editor',
defaultValue: savedDraft, // 保存済みの下書きを初期値に
})
// 変更のたびに下書きをローカルへ自動保存
crepe.on((listener) => {
listener.markdownUpdated((_ctx, markdown) => {
localStorage.setItem('draft', markdown)
})
})
await crepe.create()
// 保存ボタン押下時に現在の内容を取得
document.querySelector('#save')?.addEventListener('click', () => {
const markdown = crepe.getMarkdown()
console.log('保存する内容:', markdown)
})
エディタ上ではリッチな見た目で編集しつつ、保存されるのはプレーンなMarkdown文字列。データベースにはテキストとして保存でき、静的サイトジェネレーターへの受け渡しもそのまま行えます。「編集体験はリッチに、データはポータブルに」というMarkdownエディタの理想形を素直に実現できます。
リアルタイム共同編集
複数人で同じドキュメントを編集する機能も、公式の@milkdown/plugin-collabとY.jsを組み合わせることで実現できます。以前の記事で紹介したYjsのCRDTアルゴリズムがそのまま活きる構成で、社内WikiやチームのドキュメントツールにMilkdownを採用する際の大きな武器になります。
このほかにも、LaTeX数式・図表・スラッシュコマンドのカスタマイズなど、公式プラグインだけでもかなりの範囲をカバーしています。足りなければProseMirrorのAPIまで降りて自作プラグインを書ける、という逃げ道が常に確保されているのも安心材料です。
まとめ
Milkdownについて、基本から実践的な使い方までを紹介しました。
- MilkdownはProseMirror + Remarkベースのプラグイン駆動WYSIWYG Markdownエディタフレームワーク
- 付属のCrepeを使えば、NotionライクなエディタがCSSの読み込み込みでも10行足らずで動く
- ヘッドレス設計なので、デザインシステムへの組み込みも自由自在
getMarkdown()とリスナーで、編集内容の保存・同期も簡単- Y.js連携によるリアルタイム共同編集まで公式サポート
「2ペインのMarkdownエディタからそろそろ卒業したい」「でもProseMirrorを生で書くのは避けたい」という方には、まさにちょうどいい抽象度のライブラリです。まずはCrepeをnpm installして、その書き味を体験してみてください。公式サイトのPlaygroundでブラウザから試すこともできますよ。