はじめに
Notion風のブロックエディタを自分のアプリに組み込みたい。でも、0から作るのは無理があるし、既存のWYSIWYGエディタだとあの「ブロック感」が出ない。
BlockNoteは、そんな悩みを一発で解決してくれるReact向けのブロックベースリッチテキストエディタです。GitHubで8,900スター以上、105名のコントリビューターという活発なコミュニティを持っています。
正直なところ、Tiptapを使えばある程度のことはできます。でもBlockNoteは、Tiptapの上に「ブロックエディタとしてのUI」が最初から載っている。ドラッグ&ドロップ、スラッシュメニュー、ネスティング。全部入り。30代になって思うのは、車輪の再発明をしている時間はないということですね。
BlockNoteとは
BlockNoteはProseMirrorとTiptapをベースにした「Notion風ブロックエディタ」です。「A beautiful text editor that just works」というキャッチコピーの通り、導入するだけで見た目も操作感も整ったエディタが手に入る。
TypeCellOSが開発・メンテナンスしており、主にMPL-2.0ライセンスのオープンソースです。商用利用も可能。
特筆すべきは、React以外にもVanilla JavaScript版が用意されていること。Reactに依存しないプロジェクトでも使えます。
特徴・メリット
1. UIが最初から完成している
Tiptapがヘッドレス(UIなし)なのに対して、BlockNoteはUIコンポーネントが同梱されています。
- スラッシュメニュー(
/で呼び出し) - フォーマットメニュー(テキスト選択時に表示)
- ドラッグハンドル
- ブロック追加ボタン
見た目はカスタマイズ可能だけど、デフォルトで使っても十分にモダン。時短になるのは間違いない。
2. ドラッグ&ドロップ対応
ブロックの左側にあるハンドルをつかんで、好きな場所に移動できる。Notionの操作感そのまま。
これ、自前で実装しようとすると相当面倒なんですよね。選択範囲の管理、DOM操作、アニメーション...BlockNoteなら最初から入っている。
3. ネスティング(入れ子構造)
TabキーとShift+Tabでブロックをインデント/アウトデントできる。アウトライナーとしても使える。
個人的には、この機能があるだけでドキュメントの表現力がグッと上がると思っています。
4. リアルタイム共同編集
Yjsと連携することで、Google Docsのような複数人同時編集が実現できます。カーソル位置の共有もサポート。
社内ツールやSaaSを作るなら、この機能はかなり魅力的。
5. TypeScript完全対応
型定義がしっかりしていて、エディタの補完がバチバチ効く。カスタムブロックを作るときも型安全に開発できます。
6. ダークモード対応
ライトモードとダークモードの切り替えに標準対応。テーマの切り替えも簡単。
インストール方法
React(Mantine UI版)
npm install @blocknote/core @blocknote/react @blocknote/mantine
BlockNoteはUIライブラリとして3種類から選べます:
- @blocknote/mantine: Mantine UIベース(推奨)
- @blocknote/ariakit: Ariakit ベース
- @blocknote/shadcn: shadcn/ui ベース
Vanilla JavaScript
npm install @blocknote/core @blocknote/mantine
Reactなしでも使えるのは地味に嬉しいポイント。
基本的な使い方
Reactでの最小構成
import { useCreateBlockNote } from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/core/fonts/inter.css";
import "@blocknote/mantine/style.css";
function App() {
const editor = useCreateBlockNote();
return <BlockNoteView editor={editor} />;
}
export default App;
これだけ。本当にこれだけで、Notion風のエディタが動きます。
- スラッシュメニュー:
/を入力 - フォーマット:テキストを選択
- ドラッグ&ドロップ:ブロック左のハンドルを掴む
- ネスト:Tabキー
全部動く。
初期コンテンツの設定
import { useCreateBlockNote } from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";
function App() {
const editor = useCreateBlockNote({
initialContent: [
{
type: "heading",
props: { level: 1 },
content: "ようこそBlockNoteへ",
},
{
type: "paragraph",
content: "これはブロックベースのリッチテキストエディタです。",
},
{
type: "bulletListItem",
content: "箇条書きも",
},
{
type: "bulletListItem",
content: "簡単に作れます",
},
],
});
return <BlockNoteView editor={editor} />;
}
JSONでコンテンツを定義できるので、データベースからの読み込みも楽。
変更の取得
import { useCreateBlockNote } from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";
function App() {
const editor = useCreateBlockNote();
const handleChange = () => {
// ブロック形式(JSON)で取得
const blocks = editor.document;
console.log(blocks);
// HTML形式で取得
const html = editor.blocksToHTMLLossy(editor.document);
console.log(html);
// Markdown形式で取得
const markdown = editor.blocksToMarkdownLossy(editor.document);
console.log(markdown);
};
return (
<BlockNoteView
editor={editor}
onChange={handleChange}
/>
);
}
JSON、HTML、Markdownと3形式で出力できる。用途に応じて使い分けられます。
ダークモード対応
import { useCreateBlockNote } from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";
function App() {
const editor = useCreateBlockNote();
return (
<BlockNoteView
editor={editor}
theme="dark" // "light" | "dark"
/>
);
}
themeプロパティを渡すだけ。
実践的なユースケース
社内ナレッジベース
BlockNoteの強みが一番活きるのは、ナレッジベースやドキュメント管理システム。
import { useCreateBlockNote } from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";
import { useState, useEffect } from "react";
function DocumentEditor({ documentId }: { documentId: string }) {
const [initialContent, setInitialContent] = useState(null);
useEffect(() => {
// DBからドキュメントを取得
fetchDocument(documentId).then(doc => {
setInitialContent(doc.content);
});
}, [documentId]);
const editor = useCreateBlockNote({
initialContent: initialContent || undefined,
});
const handleSave = async () => {
const content = editor.document;
await saveDocument(documentId, { content });
};
if (!initialContent) return <div>Loading...</div>;
return (
<div>
<button onClick={handleSave}>保存</button>
<BlockNoteView editor={editor} />
</div>
);
}
ブログCMS
Markdown出力ができるので、ブログのエディタとしても優秀。
function BlogEditor() {
const editor = useCreateBlockNote();
const handlePublish = async () => {
const markdown = await editor.blocksToMarkdownLossy(editor.document);
// Markdownをサーバーに送信
await publishPost({
content: markdown,
publishedAt: new Date(),
});
};
return (
<div>
<BlockNoteView editor={editor} />
<button onClick={handlePublish}>公開する</button>
</div>
);
}
リアルタイム共同編集
Yjsを使った共同編集の実装例:
import { useCreateBlockNote } from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";
import * as Y from "yjs";
import { WebrtcProvider } from "y-webrtc";
function CollaborativeEditor({ roomId }: { roomId: string }) {
const doc = new Y.Doc();
const provider = new WebrtcProvider(roomId, doc);
const editor = useCreateBlockNote({
collaboration: {
provider,
fragment: doc.getXmlFragment("document-store"),
user: {
name: "ユーザー名",
color: "#ff0000",
},
},
});
return <BlockNoteView editor={editor} />;
}
WebRTCを使えばサーバーレスで共同編集が実現できる。小規模なチームなら十分実用的。
カスタムブロックの作成
独自のブロックタイプを追加することもできます:
import { createReactBlockSpec } from "@blocknote/react";
import { defaultBlockSpecs } from "@blocknote/core";
// カスタムブロックの定義
const AlertBlock = createReactBlockSpec(
{
type: "alert",
propSchema: {
type: {
default: "info",
values: ["info", "warning", "error"],
},
},
content: "inline",
},
{
render: (props) => (
<div className={`alert alert-${props.block.props.type}`}>
{props.contentRef}
</div>
),
}
);
// エディタに追加
const editor = useCreateBlockNote({
blockSpecs: {
...defaultBlockSpecs,
alert: AlertBlock,
},
});
Tiptapの知識があれば、さらに高度なカスタマイズも可能。
注意点
バンドルサイズ
321kB(minified + gzipped)と、それなりのサイズがあります。軽量さを求めるプロジェクトには向かないかも。
ただ、機能を考えれば妥当なサイズ。リッチエディタ系は大体このくらい。
SSR対応
Next.jsで使う場合は、クライアントサイドでのみレンダリングする必要があります。
"use client";
import dynamic from "next/dynamic";
const Editor = dynamic(
() => import("@/components/Editor"),
{ ssr: false }
);
日本語入力
IME(日本語入力)との相性は良好。変換中のテキストも正しく処理されます。これ、エディタライブラリによっては地味に問題になるので、日本人開発者としては安心ポイント。
まとめ
BlockNoteを使ってみて感じたこと:
- 導入の楽さ: 本当に数行でNotion風エディタが動く
- UI完成度: デフォルトで十分にモダン
- カスタマイズ性: ブロック追加もテーマ変更も自由
- 共同編集: Yjs連携で本格的なリアルタイム編集が可能
- 型安全性: TypeScript完全対応
正直なところ、Notionライクなエディタを自前で作ろうとしたら、数ヶ月かかってもおかしくない。ドラッグ&ドロップ、ネスティング、スラッシュメニュー、フォーマットメニュー...全部作るのは現実的じゃない。
BlockNoteなら、それが数分で手に入る。QOL上がりますね。
ナレッジベース、CMS、ノーコードツールの編集機能。Notion風のエディタが必要なら、個人的にはBlockNote一択ですね。
Tiptapで1からUIを作り込む時間がない人、すぐに動くものが欲しい人には特におすすめです。