yjs-nextjs について
yjs-nextjs は、Next.js 16 App Router で Yjs による共同編集を実装するためのコーディングリファレンススキルです。Yjs 13.6.31 を対象に、同期、永続化、React 連携や、同時編集で見落としやすいデータ損失の問題を扱います。
主な機能
- 01同期構成と依存関係の選定
- 02バイナリ更新の転送と永続化
- 03共有型によるデータモデリング
- 04React のドキュメントライフサイクルと連携
- 05Undo、オフライン読み込み、カーソル
- 06Tiptap とフォームの連携
活用シーン
- 01Next.js アプリに共同編集やオフライン同期を追加
- 02同時編集による内容の消失や重複を調査
- 03Y.Doc の永続化と更新の圧縮を設計
- 04React での Yjs Provider のライフサイクルを確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ pproenca/ dot-skills --skill yjs-nextjsインストール後、Claude Code で/yjs-nextjsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
AGENTS.mdmetadata.jsonassets/templates/_template.mdreferences/_sections.mdhist-scope-undo-by-origin.mdhist-snapshots-require-gc-disabled.mdhist-wait-for-indexeddb-sync.mdhost-authorize-rooms-not-updates.mdhost-pin-the-yjs-13-track.mdhost-route-handlers-cannot-upgrade.mdhost-trpc-is-not-the-sync-channel.mdhost-vercel-lacks-instance-affinity.mdhost-websocket-server-is-not-production.mdmodel-nested-types-not-plain-objects.mdmodel-seed-the-document-once.mdmodel-yarray-has-no-move.mdmodel-ytext-only-for-prose.mdpres-awareness-is-ephemeral.mdpres-relative-positions-for-cursors.mdreact-keep-yjs-client-only.mdreact-stable-doc-and-provider.mdreact-subscribe-via-usesyncexternalstore.mdui-do-not-drive-inputs-from-the-doc.mdui-tiptap-collaboration-wiring.mdwire-base64-not-superjson.mdwire-compaction-needs-a-doc-roundtrip.mdwire-rehydrate-to-uint8array.mdwire-send-updates-not-state.md詳細
Next.js 16、tRPC、shadcn/ui での Yjs
App Router のコードベースで使う Yjs 13.6.31 について、共同編集で必要となる判断とその解決方法を、エージェントがコードの作成やレビュー時に適用できるようにまとめています。各ルールでは、誤ったデフォルトを明示して修正します。モデルがすでに正しく処理できることに関するルールはありません。
こうした問題の大半は、気づかないうちに発生します。Yjs の収束性は保たれ、エディターも動作し続けるため、データ損失が「変更が元に戻った」という形で後から表面化します。そのため、以下のルールでは、失敗を示す証拠を先に取り上げています。その多くは、根拠なく断言するのではなく、Yjs 13.6.31 を使って直接測定したものです。
適用する場面
- Next.js 16 の App Router アプリに、共同編集、プレゼンス、オフライン同期を実装する場合
- ドキュメントモデルの共有型を選択する場合や、並行編集時に編集内容が失われるモデルをレビューする場合
- 同期バックエンドの実行場所を決める場合や、Yjs プロバイダーと並行して tRPC プロシージャを接続する場合
Y.Docの状態をデータベースに永続化する場合や、保存済みの更新を圧縮する場合- Yjs ドキュメントを React にバインドする場合(プロバイダーのライフサイクル、サブスクリプション、Strict Mode、SSR)
- Tiptap や shadcn/ui のフォームコントロールをライブドキュメントに接続する場合
- 同期はするものの、コンテンツが重複したり、空になったり、元に戻ったりするドキュメントをデバッグする場合
このスキルでは、Next.js App Router の一般的なパターン(nextjs スキルを使用)、tRPC の一般的な使い方(trpc)、shadcn/ui のコンポーネント規約(shadcn)は扱いません。CRDT と交差する部分のみを扱います。
バージョン固定
ルールは、y-websocket 3.0.0、y-protocols 1.0.7、y-indexeddb 9.0.12 と組み合わせた Yjs 13.6.31 を対象としています。Yjs は @y/* スコープで v14 への移行中であり、複数のパッケージがすでに latest または main をそのプレリリース系列に移行しています。@y/[email protected] は yjs@^14.0.0-7 に依存しています。何かをインストールする前に、host-pin-the-yjs-13-track を参照してください。
ルールのカテゴリ
| # | カテゴリ | プレフィックス | 対象 |
|---|---|---|---|
| 1 | 同期トポロジーと依存関係 | host- | 同期ループの実行場所、構築するバックエンド、信頼境界の位置、v13 系列のバージョン |
| 2 | バイナリ転送と永続化 | wire- | Uint8Array の更新を JSON 境界越しに移動し、ストレージに保存する方法 |
| 3 | 共有型での状態モデリング | model- | フィールドごとに使う共有型。並行編集のマージと相互破壊を左右します |
| 4 | ドキュメントのライフサイクルと React バインディング | react- | レンダリングをまたいだ単一ドキュメント、Strict Mode、サブスクリプション、サーバー境界 |
| 5 | Undo、スナップショット、オフライン読み込み | hist- | オリジン単位の履歴、ガベージコレクション、読み込み順序 |
| 6 | アウェアネスとカーソル | pres- | 一時的なプレゼンスと、並行編集後も維持される位置 |
| 7 | エディターとフォームの統合 | ui- | ライブドキュメントを使った Tiptap と shadcn/ui のフォームコントロール |
クイックリファレンス
1. 同期トポロジーと依存関係
host-route-handlers-cannot-upgrade— App Router にはソケットをアップグレードする API がありません。同期サーバーはリクエストとは独立して稼働する必要がありますhost-vercel-lacks-instance-affinity— Vercel は WebSocket を提供しますが、ルームを特定のインスタンスに固定しません。インメモリレジストリでは、1つのドキュメントが2つに分裂しますhost-websocket-server-is-not-production— リファレンスサーバーは出発点にすぎず、YPERSISTENCEはすでに存在しませんhost-pin-the-yjs-13-track—latestをインストールすると、v13 アプリに Yjs 14 のプレリリースが取り込まれますhost-trpc-is-not-the-sync-channel— SSE は一方向で、バイナリ入力は POST のみです。認可とスナップショットには tRPC を使用してくださいhost-authorize-rooms-not-updates— CRDT の更新はスキーマ検証も権限チェックもできないため、ルームが信頼境界になります
2. バイナリの転送と永続化
wire-rehydrate-to-uint8array—applyUpdateはnumber[]を受け付けますが、何も適用せずに黙って処理を終えますwire-base64-not-superjson— superjson で型付き配列を往復させるとペイロードは 3.69 倍になり、base64 では 1.33 倍になりますwire-send-updates-not-state— 1 回のキー入力に対し、差分は 24 バイト、状態全体は 5041 バイトですwire-compaction-needs-a-doc-roundtrip—mergeUpdatesは再エンコードしますが、削除済みの内容は回収しません
3. 共有型での状態のモデル化
model-nested-types-not-plain-objects—Y.Map内のプレーンオブジェクトは 1 つの競合単位となるため、異なるフィールドへの同時編集では片方が失われますmodel-yarray-has-no-move— 2 人が同時に行をドラッグすると、削除してから挿入する方法では行が重複しますmodel-ytext-only-for-prose— 2 人がY.Textのタイトルをそれぞれ入力し直すと、両方の値がつなぎ合わされますmodel-seed-the-document-once— 空かどうかのチェックはすべてのクライアントで通るため、テンプレートが 2 回挿入されます
4. ドキュメントのライフサイクルと React バインディング
react-stable-doc-and-provider— Strict Mode ではセットアップ、クリーンアップ、セットアップの順に実行されるため、1 つのエフェクト内で作成と破棄を管理しますreact-subscribe-via-usesyncexternalstore— 公式のバインディングは存在せず、toJSON()は呼び出すたびに新しい参照を返しますreact-keep-yjs-client-only— プロバイダーにはブラウザーのグローバル変数が必要であり、'use client'を指定してもサーバー上でレンダリングされます
5. 元に戻す、スナップショット、オフライン読み込み
hist-scope-undo-by-origin— デフォルトの元に戻すマネージャーはリモート編集も追跡するため、同僚の作業を元に戻してしまいますhist-snapshots-require-gc-disabled— これは構築時に決まります。そうでない場合、スナップショットの復元時にエラーが発生しますhist-wait-for-indexeddb-sync— 読み込み前のドキュメントと空のドキュメントは区別できません
6. アウェアネスとカーソル
pres-awareness-is-ephemeral— ドキュメント内のプレゼンスは永続的で、バージョン管理され、クリーンアップされませんpres-relative-positions-for-cursors— 誰かがその位置より前に入力した時点で、インデックスは同じ場所を示さなくなります
7. エディターとフォームの統合
ui-do-not-drive-inputs-from-the-doc—valueを割り当てるとフィールドの末尾にキャレットが移動する仕様ですui-tiptap-collaboration-wiring— Tiptap v3 は独自のフォークを使用し、contentは読み込むたびに再シードされます
使い方
判断が必要になったら、該当するリファレンスファイルを読みます。各ルールでは、修正対象となる誤ったデフォルトを示したうえで、標準的な方法を説明します。誤った方法が実際に陥りやすい落とし穴である場合は、誤りと正しい方法を対比しています。
コードを書くのではなくデバッグする場合は、症状から始めます。
| 症状 | 最初に読むもの |
|---|---|
| ドキュメントが空白で読み込まれる | wire-rehydrate-to-uint8array、hist-wait-for-indexeddb-sync |
| コンテンツが二重に表示される | model-seed-the-document-once、ui-tiptap-collaboration-wiring |
| 誰かの編集内容が通知なく元に戻る | model-nested-types-not-plain-objects、hist-scope-undo-by-origin |
| リスト項目をドラッグすると重複する | model-yarray-has-no-move |
| 2つのエディターがお互いを認識しない | host-vercel-lacks-instance-affinity |
| ユーザーが編集できるはずのないフィールドを編集した | host-authorize-rooms-not-updates |
| 共同編集者が退出した後もアバターが残る | pres-awareness-is-ephemeral |
| 入力中にキャレットが末尾へ飛ぶ | ui-do-not-drive-inputs-from-the-doc |
| 無限レンダリングループまたはスナップショットエラーが発生する | react-subscribe-via-usesyncexternalstore |
| 本番環境では動くが、開発環境では動かない | react-stable-doc-and-provider |
リファレンスファイル
| ファイル | 説明 |
|---|---|
| references/_sections.md | カテゴリーの定義と順序 |
| assets/templates/_template.md | 新しいルール用のテンプレート |
| AGENTS.md | すべてのルールをまとめた自動生成の目次 |
| metadata.json | バージョンとソースの参照情報 |