「コラボレーションドキュメント編集アプリを作りたい」

Google Docsのように複数人でリアルタイム編集できて、AIが@aiでディスカッションに参加する。そんなアプリを構想したときの技術選定の記録。

リアルタイム同期の選択肢

Liveblocks

Liveblocksはリアルタイムコラボレーション機能を提供するインフラサービス。CloudflareのDurable ObjectsとWebSocketを使って「ルーム」を構築している。

// Liveblocksの基本的な使い方
import { createClient } from "@liveblocks/client";

const client = createClient({
  publicApiKey: "pk_xxx",
});

// ルームに接続
const { room } = client.enterRoom("my-document", {
  initialPresence: { cursor: null },
});

特徴:

  • ライブカーソルが60fpsで動作するほど低レイテンシー
  • Presence機能(誰がオンラインで、どこを編集しているか)
  • Comments、AI Copilot機能が組み込み
  • BlockNote、Tiptapとの公式統合パッケージあり

Yjs

オープンソースのCRDT(Conflict-free Replicated Data Type)ライブラリ。Notion、Figmaなど大規模プロダクトでの実績がある。

import * as Y from 'yjs';
import { WebsocketProvider } from 'y-websocket';

const ydoc = new Y.Doc();
const provider = new WebsocketProvider('wss://my-server.com', 'room-id', ydoc);

// ドキュメントの変更を監視
ydoc.on('update', () => {
  console.log('Document updated');
});

特徴:

  • オープンソースで自由度が高い
  • オフライン編集対応(再接続時に自動同期)
  • 自前でWebSocketサーバーを建てる必要がある

比較

観点 Liveblocks Yjs
導入コスト 低(SaaS) 中(サーバー構築必要)
月額費用 あり インフラ費用のみ
カスタマイズ性
オフライン対応
学習コスト

結論: 早く始めたいならLiveblocks、自由度が必要ならYjs。

エディタの選択

リアルタイム同期と組み合わせるエディタ。

Tiptap

ProseMirrorベースのヘッドレスエディタ。

import { useEditor, EditorContent } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import Collaboration from '@tiptap/extension-collaboration';
import * as Y from 'yjs';

const ydoc = new Y.Doc();

const editor = useEditor({
  extensions: [
    StarterKit,
    Collaboration.configure({
      document: ydoc,
    }),
  ],
});

特徴:

  • 拡張性が高い(メンション、コメントなど追加可能)
  • Liveblocks、Yjsとの統合がスムーズ
  • TypeScriptサポートが良好

BlockNote

Notionライクなブロックエディタ。

import { useBlockNote } from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";

const editor = useBlockNote({
  collaboration: {
    provider: liveblocksProvider,
    fragment: ydoc.getXmlFragment("document"),
    user: { name: "User", color: "#ff0000" },
  },
});

特徴:

  • Notionに慣れたユーザーに馴染みやすいUI
  • スラッシュコマンド、ドラッグ&ドロップが標準
  • ブロック単位の操作に特化

選択のポイント

  • Tiptap: カスタマイズ重視、細かい制御が必要
  • BlockNote: Notionライクな体験を早く実現したい

AIインテグレーション

@aiでAIを呼び出してテキスト生成するイメージ。

アーキテクチャ

ユーザーが@aiを入力
    ↓
メンションをトリガーとして検出
    ↓
Claude API呼び出し(ストリーミング)
    ↓
生成結果をエディタに挿入

実装イメージ

// Tiptapでメンション拡張を作成
import { Extension } from '@tiptap/core';
import Mention from '@tiptap/extension-mention';

const AIMention = Mention.configure({
  HTMLAttributes: { class: 'ai-mention' },
  suggestion: {
    items: ({ query }) => {
      if (query.toLowerCase() === 'ai') {
        return [{ id: 'ai', label: 'AI Assistant' }];
      }
      return [];
    },
    command: ({ editor, range, props }) => {
      // @aiが確定されたらAPIを呼び出す
      handleAIMention(editor, range);
    },
  },
});

async function handleAIMention(editor, range) {
  const context = getDocumentContext(editor);

  const response = await fetch('/api/ai/generate', {
    method: 'POST',
    body: JSON.stringify({ context, prompt: '...' }),
  });

  // ストリーミングで結果を挿入
  const reader = response.body.getReader();
  // ...
}

注意点

「AIとディスカッション」ではなく「AIを文書生成ツールとして使う」

当初は「AIがディスカッションに参加する」イメージだったが、考え直した。

  • 人間同士がリアルタイムで共同編集・議論
  • @aiでAIを呼び出してテキスト生成(要約、下書き、リライト)
  • 生成結果をそのまま文書に挿入

AIはあくまでツール。議論自体は人間同士で行う。

ドキュメント同期の課題:Git連携

「PM/ディレクターがドキュメントを編集して、それが開発リポジトリと同期される」

これが意外と難しい。

Google Docs ↔ Git

Google DocsからGitリポジトリへの一方向同期は可能。

import { google } from 'googleapis';

const drive = google.drive({ version: 'v3', auth });

async function exportAsMarkdown(fileId: string): Promise<string> {
  // HTMLとしてエクスポート
  const res = await drive.files.export({
    fileId,
    mimeType: 'text/html',
  });

  // HTMLをMarkdownに変換
  const markdown = turndownService.turndown(res.data);
  return markdown;
}

問題: 双方向同期が実質不可能。Google Docs APIでの書き込みは「全置換」になり、差分マージができない。

Notion ↔ Git

Notionは読み書き両方のAPIがある。

import { Client } from '@notionhq/client';
import { NotionToMarkdown } from 'notion-to-md';
import { markdownToBlocks } from '@tryfabric/martian';

// Notion → Markdown
const n2m = new NotionToMarkdown({ notionClient: notion });
const mdBlocks = await n2m.pageToMarkdown(pageId);
const markdown = n2m.toMarkdownString(mdBlocks).parent;

// Markdown → Notion
const blocks = markdownToBlocks(markdown);
await notion.blocks.children.append({
  block_id: pageId,
  children: blocks,
});

問題: コンフリクト解決は自前実装が必要。画像URLの期限切れ対策も必要。

GitBook

Git連携がネイティブ。設定だけで双方向同期が動く。

# .gitbook.yaml
root: ./docs/
structure:
  readme: README.md
  summary: SUMMARY.md
PM/ディレクター: GitBookのWeb UIで編集 → 自動でGitHubにcommit
開発者: ローカルでMarkdown編集 → push → 自動でGitBookに反映

コンフリクト: Gitの仕組みで処理される(通常のGitコンフリクト)。

比較まとめ

ツール リポジトリ → ツール ツール → リポジトリ 双方向
Google Docs △ 大変 実質無理
Notion ○ 可能
GitBook ◎ ネイティブ

サービスとしての差別化

「これ、Notionでいいんじゃない?」

その通り。差別化がないとNotionに勝てない。

ニッチ特化の方向性

技術系

  • API仕様書 + 実際のエンドポイントテスト連動
  • インシデント対応のRunbook(Slack/PagerDuty連携)

業務特化

  • 契約書レビュー(変更履歴 + AI差分チェック)
  • 議事録 → タスク自動抽出 → Jira/Linear連携

クリエイティブ系

  • シナリオ/脚本執筆(キャラ設定AIが一貫性チェック)
  • 翻訳ワークフロー(原文と訳文並べて複数人でレビュー)

規制/コンプライアンス系

  • 監査証跡が必要なドキュメント
  • 医療/法務で「誰がいつ何を編集したか」厳密に残す

推奨スタック

MVP(3〜4ヶ月)を目指すなら:

const stack = {
  frontend: {
    framework: "Next.js 14 (App Router)",
    editor: "Tiptap or BlockNote",
    realtime: "Liveblocks (SaaS)",
    styling: "Tailwind CSS",
  },
  backend: {
    ai: "Claude API (ストリーミング)",
    auth: "Clerk or Supabase Auth",
    database: "PostgreSQL (Supabase)",
  },
};

まとめ

リアルタイムコラボ文書編集は、技術的には十分実現可能になっている。Liveblocks + Tiptap/BlockNoteの組み合わせで、Google Docsライクな体験は比較的早く作れる。

難しいのは「Notionでいいじゃん」を乗り越える差別化。ニッチに振り切って、特定領域の深い課題を解決するアプローチが現実的だ。

Git連携については、双方向同期を本気でやるならGitBookが最も楽。Notionは自前実装すればできるが、コンフリクト解決の設計が必要。Google Docsは一方向に割り切るのが吉。