Skip to main content

Liveblocks vs PartyKit vs Hocuspocus 2026

·PkgPulse Team
0

Liveblocks vs PartyKit vs Hocuspocus: Real-Time Collaboration Backends 2026

TL;DR

Building real-time collaborative features — multiplayer cursors, shared documents, live presence — requires infrastructure that most apps don't build themselves. Liveblocks is the complete managed collaboration platform: rooms, presence, LiveObject/LiveList data structures, comments, notifications, and AI copilot features — all as a managed service. PartyKit runs collaboration logic on Cloudflare Workers — you write the server logic, deploy globally, and it handles WebSocket connections at the edge. Hocuspocus is the Yjs-native WebSocket server — purpose-built for document collaboration (rich text editors, whiteboards) with complete self-hosting. For managed presence + cursors + comments: Liveblocks. For edge-deployed custom collaboration logic: PartyKit. For Yjs-powered document sync, self-hosted: Hocuspocus.

Key Takeaways

  • Liveblocks has the richest feature set — presence, storage, comments, notifications, AI integration, all pre-built
  • PartyKit runs on Cloudflare's edge — 300+ global locations, WebSocket connections are geographically close to users
  • Hocuspocus is the reference Yjs server — used in production by Tiptap's hosted collaboration
  • Yjs is the CRDT foundation — Liveblocks and Hocuspocus use it; PartyKit lets you bring your own CRDT
  • Liveblocks free tier: 50 MAU — generous for prototyping; pricing scales with active users
  • PartyKit is now part of Cloudflare — officially acquired in 2024, deeply integrated
  • All three have React hooksuseOthers(), useRoom(), and mutation helpers

Why Collaboration Infrastructure is Hard

Real-time collaboration requires more than WebSockets:

  • CRDT merge conflict resolution — two users typing simultaneously must produce consistent result
  • Presence — showing who's in the room, where their cursor is, what they're editing
  • Persistence — reconnecting users get the current state, not a blank slate
  • Global latency — 200ms input latency makes collaborative editing feel broken
  • Connection management — reconnects, offline queuing, connection state UI

Liveblocks: The Managed Collaboration Platform

Liveblocks provides the complete stack: infrastructure, data structures, React hooks, and pre-built UI components. You focus on your product; they handle the collaboration layer.

Installation

npm install @liveblocks/client @liveblocks/react

Room Setup

// liveblocks.config.ts — define types for your collaboration data
import { createClient } from "@liveblocks/client";
import { createRoomContext } from "@liveblocks/react";

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

// Define your collaborative data structures
type Presence = {
  cursor: { x: number; y: number } | null;
  name: string;
  color: string;
};

type Storage = {
  canvasObjects: LiveList<CanvasObject>;
  documentTitle: LiveObject<{ title: string }>;
};

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

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

Presence and Cursors

// components/CollaborativeCanvas.tsx
import { useCallback, useEffect } from "react";
import { RoomProvider, useOthers, useMyPresence } from "@/liveblocks.config";

function Cursors() {
  const others = useOthers();

  return (
    <>
      {others.map(({ connectionId, presence }) => {
        if (!presence.cursor) return null;

        return (
          <div
            key={connectionId}
            style={{
              position: "absolute",
              left: presence.cursor.x,
              top: presence.cursor.y,
              pointerEvents: "none",
              transform: "translate(-4px, -4px)",
            }}
          >
            <svg width="24" height="24" viewBox="0 0 24 24">
              <path
                fill={presence.color}
                d="M5.65 1.27L5.26.86 0 15.18l4.6-1.56 2.94 5.06 1.73-1 -2.94-5.06 4.6-1.56L5.65 1.27z"
              />
            </svg>
            <span
              style={{
                background: presence.color,
                color: "white",
                borderRadius: "4px",
                padding: "2px 6px",
                fontSize: "12px",
                marginTop: "2px",
              }}
            >
              {presence.name}
            </span>
          </div>
        );
      })}
    </>
  );
}

function Canvas() {
  const [_, updateMyPresence] = useMyPresence();

  const handleMouseMove = useCallback(
    (event: React.MouseEvent) => {
      const rect = event.currentTarget.getBoundingClientRect();
      updateMyPresence({
        cursor: {
          x: event.clientX - rect.left,
          y: event.clientY - rect.top,
        },
      });
    },
    [updateMyPresence]
  );

  return (
    <div
      className="relative w-full h-[600px] bg-gray-50"
      onMouseMove={handleMouseMove}
      onMouseLeave={() => updateMyPresence({ cursor: null })}
    >
      <Cursors />
      {/* Your canvas content */}
    </div>
  );
}

export function CollaborativeCanvas({ roomId }: { roomId: string }) {
  return (
    <RoomProvider
      id={roomId}
      initialPresence={{ cursor: null, name: "Anonymous", color: "#3b82f6" }}
    >
      <Canvas />
    </RoomProvider>
  );
}

LiveStorage (Shared Data Structures)

import { LiveList, LiveObject } from "@liveblocks/client";
import { useStorage, useMutation } from "@/liveblocks.config";

// Shared todo list — all users see updates in real-time
function CollaborativeTodos() {
  const todos = useStorage((root) => root.todos);

  const addTodo = useMutation(({ storage }, text: string) => {
    const todos = storage.get("todos");
    todos.push({ id: Date.now(), text, completed: false });
  }, []);

  const toggleTodo = useMutation(({ storage }, id: number) => {
    const todos = storage.get("todos");
    const index = todos.findIndex((t) => t.id === id);
    if (index !== -1) {
      const todo = todos.get(index);
      todos.set(index, { ...todo, completed: !todo.completed });
    }
  }, []);

  return (
    <div>
      {todos?.map((todo) => (
        <div key={todo.id} onClick={() => toggleTodo(todo.id)}>
          <input type="checkbox" checked={todo.completed} readOnly />
          <span style={{ textDecoration: todo.completed ? "line-through" : "none" }}>
            {todo.text}
          </span>
        </div>
      ))}
      <button onClick={() => addTodo("New task")}>Add Task</button>
    </div>
  );
}

PartyKit: Edge-Deployed Collaboration

PartyKit runs your WebSocket server logic on Cloudflare Workers globally. You write a JavaScript class, deploy it, and connect from any client.

Installation

npm install partykit partysocket
npx partykit init my-collab-app

PartyKit Server

// party/index.ts — runs on Cloudflare's edge
import type * as Party from "partykit/server";

type Connection = Party.Connection<{ user: string; color: string }>;

export default class CollabRoom implements Party.Server {
  constructor(readonly room: Party.Room) {}

  // Track connected users
  private users = new Map<string, { name: string; cursor: { x: number; y: number } | null }>();

  onConnect(conn: Connection, ctx: Party.ConnectionContext) {
    const url = new URL(ctx.request.url);
    const name = url.searchParams.get("name") ?? "Anonymous";

    this.users.set(conn.id, { name, cursor: null });

    // Send current state to the new connection
    conn.send(JSON.stringify({
      type: "init",
      users: Object.fromEntries(this.users),
    }));

    // Notify others
    this.room.broadcast(
      JSON.stringify({ type: "user_joined", id: conn.id, name }),
      [conn.id]  // Exclude the new user
    );
  }

  onMessage(message: string, sender: Connection) {
    const data = JSON.parse(message);

    if (data.type === "cursor_move") {
      const user = this.users.get(sender.id);
      if (user) {
        user.cursor = data.cursor;
        // Broadcast cursor position to all other connections
        this.room.broadcast(
          JSON.stringify({
            type: "cursor_update",
            id: sender.id,
            cursor: data.cursor,
            name: user.name,
          }),
          [sender.id]
        );
      }
    }

    if (data.type === "draw") {
      // Broadcast drawing operations to all connections
      this.room.broadcast(message, [sender.id]);
    }
  }

  onClose(conn: Connection) {
    this.users.delete(conn.id);
    this.room.broadcast(
      JSON.stringify({ type: "user_left", id: conn.id })
    );
  }
}

PartyKit Client (React)

import { useEffect, useRef, useState } from "react";
import PartySocket from "partysocket";

function usePartySocket(roomId: string, userName: string) {
  const [users, setUsers] = useState<Record<string, any>>({});
  const socketRef = useRef<PartySocket | null>(null);

  useEffect(() => {
    const socket = new PartySocket({
      host: process.env.NEXT_PUBLIC_PARTYKIT_HOST!,  // your-app.partykit.dev
      room: roomId,
      query: { name: userName },
    });

    socketRef.current = socket;

    socket.addEventListener("message", (event) => {
      const data = JSON.parse(event.data);

      if (data.type === "init") setUsers(data.users);
      if (data.type === "user_joined") {
        setUsers((prev) => ({ ...prev, [data.id]: { name: data.name, cursor: null } }));
      }
      if (data.type === "cursor_update") {
        setUsers((prev) => ({
          ...prev,
          [data.id]: { ...prev[data.id], cursor: data.cursor },
        }));
      }
      if (data.type === "user_left") {
        setUsers((prev) => {
          const next = { ...prev };
          delete next[data.id];
          return next;
        });
      }
    });

    return () => socket.close();
  }, [roomId, userName]);

  const sendCursorMove = (x: number, y: number) => {
    socketRef.current?.send(JSON.stringify({
      type: "cursor_move",
      cursor: { x, y },
    }));
  };

  return { users, sendCursorMove };
}

Hocuspocus: Yjs-Native Document Sync

Hocuspocus is the WebSocket server designed specifically for Yjs — the CRDT library used by TipTap, BlockNotes, and other collaborative editors. Self-host it and get document sync, persistence, and authentication.

Installation

npm install @hocuspocus/server @hocuspocus/extension-database

Hocuspocus Server Setup

// server.ts — Hocuspocus with database persistence
import { Server } from "@hocuspocus/server";
import { Database } from "@hocuspocus/extension-database";
import { Logger } from "@hocuspocus/extension-logger";
import * as Y from "yjs";

const server = Server.configure({
  port: 1234,

  extensions: [
    new Logger(),

    new Database({
      // Load document from database
      fetch: async ({ documentName }) => {
        const doc = await db.documents.findUnique({
          where: { name: documentName },
        });
        return doc?.content ?? null;  // Uint8Array or null
      },

      // Store document to database
      store: async ({ documentName, state }) => {
        await db.documents.upsert({
          where: { name: documentName },
          create: { name: documentName, content: state },
          update: { content: state },
        });
      },
    }),
  ],

  // Authentication
  async onAuthenticate(data) {
    const { token } = data;
    const user = await verifyToken(token);

    if (!user) {
      throw new Error("Unauthorized");
    }

    // Pass user data to be available in other hooks
    return { user };
  },

  // Authorization per document
  async onLoadDocument(data) {
    const { documentName, context } = data;
    const hasAccess = await checkAccess(context.user.id, documentName);

    if (!hasAccess) {
      throw new Error("Forbidden");
    }
  },
});

server.listen();

React Integration with TipTap

// CollaborativeEditor.tsx — TipTap + Hocuspocus
import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Collaboration from "@tiptap/extension-collaboration";
import CollaborationCursor from "@tiptap/extension-collaboration-cursor";
import { HocuspocusProvider } from "@hocuspocus/provider";
import * as Y from "yjs";
import { useEffect, useState } from "react";

const ydoc = new Y.Doc();

const provider = new HocuspocusProvider({
  url: "ws://localhost:1234",
  name: "document:123",
  document: ydoc,
  token: userAuthToken,

  onConnect: () => console.log("Connected"),
  onDisconnect: () => console.log("Disconnected"),
  onSynced: () => console.log("Synced with server"),
});

export function CollaborativeEditor() {
  const [synced, setSynced] = useState(false);

  const editor = useEditor({
    extensions: [
      StarterKit.configure({ history: false }),
      Collaboration.configure({ document: ydoc }),
      CollaborationCursor.configure({
        provider,
        user: { name: currentUser.name, color: currentUser.color },
      }),
    ],
  });

  useEffect(() => {
    provider.on("synced", () => setSynced(true));
    return () => provider.disconnect();
  }, []);

  if (!synced) return <div>Connecting...</div>;

  return (
    <div>
      <EditorContent editor={editor} />
    </div>
  );
}

Feature Comparison

FeatureLiveblocksPartyKitHocuspocus
HostingManaged SaaSCloudflare EdgeSelf-hosted
Yjs supportBring your own✅ Native
Presence/cursors✅ Built-inCustom codeVia Y.Awareness
Comments system✅ Pre-builtCustom code
Notifications✅ Pre-builtCustom code
Data persistence✅ AutomaticManual✅ Extensions
Global edgeVia CDN✅ 300+ PoPsSelf-deploy
AuthenticationCustom
React hooks✅ Full suiteManualVia HocuspocusProvider
Open source✅ MIT
Self-hostable✅ (limited)✅ Full
GitHub stars~5k~4k~3k
Free tier50 MAUPay per requestSelf-hosted
Rich text supportVia YjsCustom✅ Native (TipTap)

When to Use Each

Choose Liveblocks if:

  • You want presence, cursors, shared data, comments, and notifications without building backend logic
  • Managed infrastructure with 99.99% uptime SLA is required
  • You need AI copilot features (Liveblocks AI extension)
  • Time-to-market matters more than cost at scale

Choose PartyKit if:

  • Your collaboration logic doesn't fit standard CRDT patterns (games, custom protocols)
  • Global edge latency is critical (Cloudflare's 300+ edge locations)
  • You want to own and customize the server logic completely
  • Cost predictability via Cloudflare's per-request pricing model

Choose Hocuspocus if:

  • You're building a rich text editor or document editor with TipTap/ProseMirror
  • Self-hosting is required for compliance or data residency
  • You want to integrate with your existing WebSocket infrastructure
  • The Yjs ecosystem (whiteboards, code editors, spreadsheets) is your primary use case

Ecosystem and Community Health

Liveblocks has received significant investment and has grown its team substantially through 2025. The managed platform approach means that product features — new data structure types, notifications, comments, AI integration — ship on Liveblocks's roadmap without any action from developers using the library. The React SDK is polished and well-typed, and the documentation is among the best in the collaboration infrastructure space. The 5k GitHub stars understate Liveblocks's actual production adoption, as many enterprise users sign NDAs and don't appear in public case studies.

PartyKit's integration with Cloudflare represents a significant change in its trajectory. Cloudflare has strong incentive to make PartyKit the standard collaboration primitive on their platform, which means engineering resources, marketing, and tight integration with Cloudflare Workers, Durable Objects, and R2. For developers already invested in Cloudflare's ecosystem, PartyKit is becoming the obvious choice for WebSocket-based collaboration features. The open-source GitHub repository with 4k stars is the single-server model; Cloudflare's managed deployment removes the operational burden.

Hocuspocus is maintained by Tiptap, the company behind the Tiptap rich text editor. The alignment is natural — Hocuspocus provides the WebSocket server that powers Tiptap's collaboration extension, and Tiptap Cloud (Hocuspocus's hosted version) is a commercial offering that gives Tiptap the revenue to sustain both projects. If you're building with Tiptap and want managed collaboration infrastructure, Tiptap Cloud is a seamless fit. For self-hosted Hocuspocus, the 3k GitHub stars represent a community of developers building document editors across many sectors — legal, medical, engineering, education.

Real-World Adoption

Liveblocks powers the collaboration layer of several notable products. Spline (3D design in browser), Framer (website builder), and Pitch (collaborative presentations) all use or have used Liveblocks-style infrastructure for their real-time features. The Liveblocks website publishes case studies including Linear and several design tool companies. The product's strength is visible in how many Y Combinator-backed tools have added multiplayer features as a competitive differentiator — Liveblocks reduces that from a month-long engineering project to a few days. For the broader realtime infrastructure comparison, see best npm packages for realtime 2026.

PartyKit's most cited production use case is the multiplayer features in collaborative coding tools and whiteboard applications. The hackathon community heavily uses PartyKit because the "write a server class, deploy globally" model fits rapid prototyping. Several notable open-source projects have added PartyKit-powered real-time features, including collaborative terminals and shared debugging sessions.

Hocuspocus underpins Tiptap Editor, which is one of the most widely deployed rich text editors in the JavaScript ecosystem. Every Tiptap installation that enables the Collaboration extension connects to a Hocuspocus server (either self-hosted or Tiptap Cloud). This makes Hocuspocus the most widely deployed Yjs server even if its GitHub stars don't reflect that. Notion-style editors, legal document drafting tools, and engineering documentation systems are the dominant use cases. For the database layer that stores collaboration state, see Cloudflare Durable Objects vs Upstash vs Turso edge databases 2026.

Developer Experience Deep Dive

Liveblocks's TypeScript experience is outstanding. The config file approach — where you define your Presence and Storage types once — gives you full type inference throughout every hook and callback. useOthers() returns properly typed cursor data, useStorage() infers the LiveObject structure, and useMutation() knows the shape of storage it can modify. For large teams where consistency across the codebase matters, this type safety is valuable. The DevTools browser extension lets you inspect the current room state, presence, and storage in real time during development.

The Liveblocks limits to understand are around the cost model. The free tier at 50 monthly active users is useful for prototyping but will hit limits quickly in a real beta. The pricing scales with MAU and features — comments and notifications push you to higher tiers. For enterprise products, the cost is typically justified by the engineering time saved, but early-stage products need to budget for it.

PartyKit's developer experience requires more upfront thinking because you're writing the server logic yourself. The simple cases are easy — the examples in the documentation cover cursors, drawing, and chat in under 100 lines. But production features like message history, reconnection state, and conflict resolution require architectural decisions. The flexibility is the point — if you need to implement a custom CRDT or a domain-specific synchronization protocol, PartyKit won't constrain you. Hocuspocus and Liveblocks, by contrast, have opinionated data models that work well for their target use cases and poorly for anything outside them.

Hocuspocus's developer experience is tightly coupled to the Yjs ecosystem. If you're already using Y.Doc, Y.Text, Y.Map, and the Yjs awareness protocol, Hocuspocus feels like a natural extension. The extensions system (Database, Logger, Throttle, Webhook) lets you add persistence and monitoring without modifying the core server. The authentication hook is straightforward — receive a token, validate it, throw if unauthorized.

Performance and Latency

Real-time collaboration latency is directly felt by users — input that takes more than 100ms to appear for other collaborators feels broken. All three solutions address latency differently.

Liveblocks uses a managed infrastructure with data centers in multiple regions. Presence updates (cursor movements) are propagated between clients in approximately 50-100ms for users on the same continent, 100-200ms cross-continent. Storage mutations using LiveObject and LiveList use optimistic updates client-side, meaning the local user sees changes immediately while propagation to other clients happens asynchronously.

PartyKit on Cloudflare has the best global latency story because Durable Objects run in the Cloudflare edge location closest to the majority of room participants. For a room where all users are in Europe, the Durable Object migrates to a European edge location, reducing latency to under 30ms for those users. This geographic affinity is automatic and requires no configuration.

Hocuspocus's latency depends entirely on your server deployment. Self-hosted on a single-region server, users geographically far from the server see 200-400ms latency. Running Tiptap Cloud or deploying Hocuspocus to multiple regions with a WebSocket-aware load balancer achieves the same latency profile as the managed options.

Final Verdict 2026

For product teams that want to ship multiplayer features quickly without owning infrastructure, Liveblocks is the correct choice. The pre-built React hooks for presence, cursors, storage, comments, and notifications cover 90% of what most products need. The TypeScript quality is excellent. The main constraint is cost at scale.

For Cloudflare Workers developers and teams that want full server-side control over their collaboration logic, PartyKit is the obvious fit. The model of writing a JavaScript class that runs on the edge is familiar and powerful, and Cloudflare's backing means the infrastructure is reliable.

For document editor developers — anyone building rich text editing with TipTap, ProseMirror, or Yjs-based whiteboards — Hocuspocus is the reference implementation. Self-host for full control, or use Tiptap Cloud for managed simplicity. The Yjs CRDT guarantees conflict-free merging that no custom protocol can easily match.

Methodology

Data sourced from GitHub repositories (star counts as of February 2026), official documentation, npm download statistics (January 2026), and community benchmarks from builder communities on Discord. Pricing data verified from official pricing pages. Feature availability verified against documentation.

Related: Best Real-Time Libraries 2026, Best WebSocket Libraries for Node.js 2026, Best Next.js Auth Solutions 2026

The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.