jpskill.com
🛠️ 開発・MCP コミュニティ

liveblocks

Liveblocksは、Webアプリにリアルタイムな共同作業機能(カーソル表示、同時編集、コメントなど)を簡単に追加できるプラットフォームで、そのReact HooksやAPIを使って開発者が機能を実装するのを支援するSkill。

📜 元の英語説明(参考)

Expert guidance for Liveblocks, the platform for adding real-time collaboration features to web applications. Helps developers implement live cursors, presence indicators, collaborative editing, comments, and notifications using Liveblocks' React hooks and APIs.

🇯🇵 日本人クリエイター向け解説

一言でいうと

Liveblocksは、Webアプリにリアルタイムな共同作業機能(カーソル表示、同時編集、コメントなど)を簡単に追加できるプラットフォームで、そのReact HooksやAPIを使って開発者が機能を実装するのを支援するSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o liveblocks.zip https://jpskill.com/download/15076.zip && unzip -o liveblocks.zip && rm liveblocks.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/15076.zip -OutFile "$d\liveblocks.zip"; Expand-Archive "$d\liveblocks.zip" -DestinationPath $d -Force; ri "$d\liveblocks.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して liveblocks.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → liveblocks フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

Liveblocks — リアルタイムコラボレーション SDK

概要

Liveblocks は、Web アプリケーションにリアルタイムコラボレーション機能を追加するためのプラットフォームです。Liveblocks の React hooks と API を使用して、ライブカーソル、プレゼンスインジケーター、共同編集、コメント、通知などを実装する開発者を支援します。

手順

ルームのセットアップとプレゼンス

ユーザープレゼンストラッキングを使用して、コラボレーションルームを構成します。

// src/liveblocks.config.ts — Liveblocks の型構成
import { createClient } from "@liveblocks/client";
import { createRoomContext, createLiveblocksContext } from "@liveblocks/react";

const client = createClient({
  publicApiKey: process.env.NEXT_PUBLIC_LIVEBLOCKS_PUBLIC_KEY!,
  // または、本番環境では認証エンドポイントを使用します (推奨)
  // authEndpoint: "/api/liveblocks-auth",
});

// コラボレーションデータの型を定義します
type Presence = {
  cursor: { x: number; y: number } | null;  // ユーザーのカーソル位置
  selectedId: string | null;                  // 現在選択されている要素
  name: string;                               // 表示名
  color: string;                               // アバター/カーソルの色
};

type Storage = {
  shapes: LiveList<Shape>;                    // 共有キャンバスの図形
  document: LiveObject<DocumentState>;        // 共有ドキュメントの状態
};

type UserMeta = {
  id: string;
  info: { name: string; avatar: string; color: string };
};

export const {
  RoomProvider,
  useMyPresence,
  useOthers,
  useStorage,
  useMutation,
  useSelf,
} = createRoomContext<Presence, Storage, UserMeta>(client);

ライブカーソル

他のユーザーのカーソル位置をリアルタイムで表示します。

// src/components/LiveCursors.tsx — 接続されているすべてのユーザーのカーソルを表示します
import { useOthers, useMyPresence } from "../liveblocks.config";
import { useCallback, useEffect } from "react";

export function LiveCursors() {
  const others = useOthers();
  const [myPresence, updateMyPresence] = useMyPresence();

  // カーソルの動きを追跡し、他のユーザーにブロードキャストします
  const handlePointerMove = useCallback(
    (e: React.PointerEvent) => {
      updateMyPresence({
        cursor: { x: e.clientX, y: e.clientY },
      });
    },
    [updateMyPresence]
  );

  const handlePointerLeave = useCallback(() => {
    updateMyPresence({ cursor: null });
  }, [updateMyPresence]);

  return (
    <div
      onPointerMove={handlePointerMove}
      onPointerLeave={handlePointerLeave}
      style={{ position: "relative", width: "100%", height: "100vh" }}
    >
      {/* 他のユーザーのカーソルをレンダリングします */}
      {others.map(({ connectionId, presence, info }) => {
        if (!presence.cursor) return null;
        return (
          <Cursor
            key={connectionId}
            x={presence.cursor.x}
            y={presence.cursor.y}
            name={info?.name ?? "Anonymous"}
            color={info?.color ?? "#000"}
          />
        );
      })}
    </div>
  );
}

function Cursor({ x, y, name, color }: { x: number; y: number; name: string; color: string }) {
  return (
    <div style={{ position: "absolute", left: x, top: y, pointerEvents: "none" }}>
      {/* SVG カーソルアイコン */}
      <svg width="24" height="24" viewBox="0 0 24 24" fill={color}>
        <path d="M5 3l14 8-6 2-2 6z" />
      </svg>
      {/* 名前ラベル */}
      <span style={{
        backgroundColor: color,
        color: "white",
        padding: "2px 8px",
        borderRadius: "4px",
        fontSize: "12px",
        whiteSpace: "nowrap",
      }}>
        {name}
      </span>
    </div>
  );
}

コラボレーティブストレージ

すべてのユーザー間で同期される共有データ構造です。

// src/components/CollaborativeCanvas.tsx — コンフリクトフリーな更新による共有キャンバス
import { useStorage, useMutation } from "../liveblocks.config";
import { LiveList, LiveObject } from "@liveblocks/client";

type Shape = {
  id: string;
  type: "rectangle" | "circle" | "text";
  x: number;
  y: number;
  width: number;
  height: number;
  fill: string;
};

export function CollaborativeCanvas() {
  // useStorage は、共有ルームストレージから読み取ります (CRDT 経由で同期されます)
  const shapes = useStorage((root) => root.shapes);

  // useMutation は、共有ストレージを変更できる関数を作成します
  // ミューテーションはアトミックでコンフリクトフリーです — 2 人のユーザーが同時に編集できます
  const addShape = useMutation(({ storage }, shape: Shape) => {
    const shapes = storage.get("shapes");
    shapes.push(new LiveObject(shape));
  }, []);

  const moveShape = useMutation(({ storage }, id: string, x: number, y: number) => {
    const shapes = storage.get("shapes");
    const shape = shapes.find((s) => s.get("id") === id);
    if (shape) {
      shape.set("x", x);    // 変更されたフィールドのみが同期されます — 帯域幅効率が良い
      shape.set("y", y);
    }
  }, []);

  const deleteShape = useMutation(({ storage }, id: string) => {
    const shapes = storage.get("shapes");
    const index = shapes.findIndex((s) => s.get("id") === id);
    if (index !== -1) shapes.delete(index);
  }, []);

  return (
    <canvas>
      {shapes?.map((shape) => (
        <CanvasShape
          key={shape.id}
          shape={shape}
          onMove={(x, y) => moveShape(shape.id, x, y)}
          onDelete={() => deleteShape(shape.id)}
        />
      ))}
    </canvas>
  );
}

コメントとスレッド

アプリケーションの任意の部分にコメントスレッドを追加します。

// src/components/Comments.tsx — ドキュメント要素のインラインコメント
import { useThreads, useCreateThread, useCreateComment } from "@liveblocks/react/suspense";

export function CommentsSidebar({ elementId }: { elementId: string }) {
  // このルームのすべてのコメントスレッドをフェッチします
  const { threads } = useThreads();
  const createThread = useCreateThread();
  const createComment = useCreateComment();

  // 選択された要素にアタッチされたスレッドをフィルタリングします
  const elementThreads = threads.filter(
    (thread) => thread.metadata.elementId === elementId
  );

  const handleNewThread = (body: string) => {
    createThread({
      b
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Liveblocks — Real-Time Collaboration SDK

Overview

Liveblocks, the platform for adding real-time collaboration features to web applications. Helps developers implement live cursors, presence indicators, collaborative editing, comments, and notifications using Liveblocks' React hooks and APIs.

Instructions

Room Setup and Presence

Configure a collaborative room with user presence tracking:

// src/liveblocks.config.ts — Liveblocks type configuration
import { createClient } from "@liveblocks/client";
import { createRoomContext, createLiveblocksContext } from "@liveblocks/react";

const client = createClient({
  publicApiKey: process.env.NEXT_PUBLIC_LIVEBLOCKS_PUBLIC_KEY!,
  // Or use auth endpoint for production (recommended)
  // authEndpoint: "/api/liveblocks-auth",
});

// Define types for your collaborative data
type Presence = {
  cursor: { x: number; y: number } | null;  // User's cursor position
  selectedId: string | null;                  // Currently selected element
  name: string;                               // Display name
  color: string;                              // Avatar/cursor color
};

type Storage = {
  shapes: LiveList<Shape>;                    // Shared canvas shapes
  document: LiveObject<DocumentState>;        // Shared document state
};

type UserMeta = {
  id: string;
  info: { name: string; avatar: string; color: string };
};

export const {
  RoomProvider,
  useMyPresence,
  useOthers,
  useStorage,
  useMutation,
  useSelf,
} = createRoomContext<Presence, Storage, UserMeta>(client);

Live Cursors

Show other users' cursor positions in real time:

// src/components/LiveCursors.tsx — Display cursors of all connected users
import { useOthers, useMyPresence } from "../liveblocks.config";
import { useCallback, useEffect } from "react";

export function LiveCursors() {
  const others = useOthers();
  const [myPresence, updateMyPresence] = useMyPresence();

  // Track cursor movement and broadcast to other users
  const handlePointerMove = useCallback(
    (e: React.PointerEvent) => {
      updateMyPresence({
        cursor: { x: e.clientX, y: e.clientY },
      });
    },
    [updateMyPresence]
  );

  const handlePointerLeave = useCallback(() => {
    updateMyPresence({ cursor: null });
  }, [updateMyPresence]);

  return (
    <div
      onPointerMove={handlePointerMove}
      onPointerLeave={handlePointerLeave}
      style={{ position: "relative", width: "100%", height: "100vh" }}
    >
      {/* Render other users' cursors */}
      {others.map(({ connectionId, presence, info }) => {
        if (!presence.cursor) return null;
        return (
          <Cursor
            key={connectionId}
            x={presence.cursor.x}
            y={presence.cursor.y}
            name={info?.name ?? "Anonymous"}
            color={info?.color ?? "#000"}
          />
        );
      })}
    </div>
  );
}

function Cursor({ x, y, name, color }: { x: number; y: number; name: string; color: string }) {
  return (
    <div style={{ position: "absolute", left: x, top: y, pointerEvents: "none" }}>
      {/* SVG cursor icon */}
      <svg width="24" height="24" viewBox="0 0 24 24" fill={color}>
        <path d="M5 3l14 8-6 2-2 6z" />
      </svg>
      {/* Name label */}
      <span style={{
        backgroundColor: color,
        color: "white",
        padding: "2px 8px",
        borderRadius: "4px",
        fontSize: "12px",
        whiteSpace: "nowrap",
      }}>
        {name}
      </span>
    </div>
  );
}

Collaborative Storage

Shared data structures that sync across all users:

// src/components/CollaborativeCanvas.tsx — Shared canvas with conflict-free updates
import { useStorage, useMutation } from "../liveblocks.config";
import { LiveList, LiveObject } from "@liveblocks/client";

type Shape = {
  id: string;
  type: "rectangle" | "circle" | "text";
  x: number;
  y: number;
  width: number;
  height: number;
  fill: string;
};

export function CollaborativeCanvas() {
  // useStorage reads from the shared room storage (synced via CRDT)
  const shapes = useStorage((root) => root.shapes);

  // useMutation creates a function that can modify shared storage
  // Mutations are atomic and conflict-free — two users can edit simultaneously
  const addShape = useMutation(({ storage }, shape: Shape) => {
    const shapes = storage.get("shapes");
    shapes.push(new LiveObject(shape));
  }, []);

  const moveShape = useMutation(({ storage }, id: string, x: number, y: number) => {
    const shapes = storage.get("shapes");
    const shape = shapes.find((s) => s.get("id") === id);
    if (shape) {
      shape.set("x", x);    // Only the changed field syncs — bandwidth efficient
      shape.set("y", y);
    }
  }, []);

  const deleteShape = useMutation(({ storage }, id: string) => {
    const shapes = storage.get("shapes");
    const index = shapes.findIndex((s) => s.get("id") === id);
    if (index !== -1) shapes.delete(index);
  }, []);

  return (
    <canvas>
      {shapes?.map((shape) => (
        <CanvasShape
          key={shape.id}
          shape={shape}
          onMove={(x, y) => moveShape(shape.id, x, y)}
          onDelete={() => deleteShape(shape.id)}
        />
      ))}
    </canvas>
  );
}

Comments and Threads

Add comment threads to any part of your application:

// src/components/Comments.tsx — Inline comments on document elements
import { useThreads, useCreateThread, useCreateComment } from "@liveblocks/react/suspense";

export function CommentsSidebar({ elementId }: { elementId: string }) {
  // Fetch all comment threads for this room
  const { threads } = useThreads();
  const createThread = useCreateThread();
  const createComment = useCreateComment();

  // Filter threads attached to the selected element
  const elementThreads = threads.filter(
    (thread) => thread.metadata.elementId === elementId
  );

  const handleNewThread = (body: string) => {
    createThread({
      body: { version: 1, content: [{ type: "paragraph", children: [{ text: body }] }] },
      metadata: { elementId, resolved: false },
    });
  };

  return (
    <div className="comments-sidebar">
      <h3>Comments</h3>
      {elementThreads.map((thread) => (
        <ThreadView
          key={thread.id}
          thread={thread}
          onReply={(body) => createComment({ threadId: thread.id, body })}
        />
      ))}
      <NewThreadForm onSubmit={handleNewThread} />
    </div>
  );
}

Authentication Endpoint

Secure room access with token-based auth:

// app/api/liveblocks-auth/route.ts — Next.js auth endpoint
import { Liveblocks } from "@liveblocks/node";
import { NextRequest, NextResponse } from "next/server";
import { getSession } from "@/lib/auth";

const liveblocks = new Liveblocks({
  secret: process.env.LIVEBLOCKS_SECRET_KEY!,
});

export async function POST(request: NextRequest) {
  const session = await getSession();
  if (!session?.user) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  // Prepare Liveblocks session with user identity
  const liveblocksSession = liveblocks.prepareSession(session.user.id, {
    userInfo: {
      name: session.user.name,
      avatar: session.user.image,
      color: generateColor(session.user.id),  // Deterministic color from user ID
    },
  });

  // Grant access to specific rooms based on your authorization logic
  const { room } = await request.json();
  const hasAccess = await checkRoomAccess(session.user.id, room);

  if (hasAccess) {
    liveblocksSession.allow(room, liveblocksSession.FULL_ACCESS);
  }

  const { status, body } = await liveblocksSession.authorize();
  return new NextResponse(body, { status });
}

Installation

# Core packages
npm install @liveblocks/client @liveblocks/react

# For Next.js with comments and notifications
npm install @liveblocks/node @liveblocks/react-ui

# Yjs integration for text editing
npm install @liveblocks/yjs yjs

Examples

Example 1: Setting up Liveblocks with a custom configuration

User request:

I just installed Liveblocks. Help me configure it for my TypeScript + React workflow with my preferred keybindings.

The agent creates the configuration file with TypeScript-aware settings, configures relevant plugins/extensions for React development, sets up keyboard shortcuts matching the user's preferences, and verifies the setup works correctly.

Example 2: Extending Liveblocks with custom functionality

User request:

I want to add a custom live cursors to Liveblocks. How do I build one?

The agent scaffolds the extension/plugin project, implements the core functionality following Liveblocks's API patterns, adds configuration options, and provides testing instructions to verify it works end-to-end.

Guidelines

  1. Use auth endpoint in production — Public API keys are fine for dev; production needs proper authentication
  2. Type your presence and storage — TypeScript generics prevent runtime errors in collaborative data
  3. Granular mutations — Update individual fields, not entire objects; Liveblocks only syncs what changed
  4. Throttle presence updates — Cursor movement fires constantly; throttle to 50-100ms intervals
  5. Handle offline gracefully — Liveblocks queues changes offline; show a connection status indicator
  6. Room naming convention — Use predictable patterns like project:{projectId}:document:{docId}
  7. Clean up storage — Delete rooms when projects are archived; storage persists indefinitely
  8. Test with multiple tabs — Open your app in 2-3 tabs during development to verify real-time sync