TL;DR
Liveblocks provides real-time collaboration infrastructure — presence, cursors, comments, and document storage. The free tier supports 1,000 monthly active users with all features included.
What Is Liveblocks?
Liveblocks provides collaboration primitives:
- Presence — show who's online, cursor positions, selections
- Storage — conflict-free real-time document syncing (CRDT)
- Comments — threaded comments with mentions and notifications
- Text Editor — collaborative rich text with Tiptap/Lexical
- Notifications — in-app notification system
- Free tier — 1,000 MAU, all features
Quick Start with React
npm install @liveblocks/client @liveblocks/react
// liveblocks.config.ts
import { createClient } from "@liveblocks/client";
import { createRoomContext } from "@liveblocks/react";
const client = createClient({
publicApiKey: "pk_YOUR_PUBLIC_KEY",
});
type Presence = {
cursor: { x: number; y: number } | null;
name: string;
};
export const { RoomProvider, useOthers, useMyPresence } =
createRoomContext<Presence>(client);
Live Cursors
import { RoomProvider, useOthers, useMyPresence } from "./liveblocks.config";
function Cursors() {
const others = useOthers();
const [myPresence, updateMyPresence] = useMyPresence();
return (
<div
style={{ width: "100vw", height: "100vh" }}
onPointerMove={(e) =>
updateMyPresence({ cursor: { x: e.clientX, y: e.clientY } })
}
onPointerLeave={() => updateMyPresence({ cursor: null })}
>
{others.map(({ connectionId, presence }) =>
presence.cursor ? (
<div
key={connectionId}
style={{
position: "absolute",
left: presence.cursor.x,
top: presence.cursor.y,
width: 10,
height: 10,
borderRadius: "50%",
background: "red",
}}
/>
) : null
)}
</div>
);
}
function App() {
return (
<RoomProvider id="my-room" initialPresence={{ cursor: null, name: "" }}>
<Cursors />
</RoomProvider>
);
}
Real-Time Storage (CRDT)
import { useMutation, useStorage } from "./liveblocks.config";
function TodoList() {
const todos = useStorage((root) => root.todos);
const addTodo = useMutation(({ storage }, text: string) => {
const todos = storage.get("todos");
todos.push({ text, completed: false });
}, []);
const toggleTodo = useMutation(({ storage }, index: number) => {
const todo = storage.get("todos").get(index);
todo.set("completed", !todo.get("completed"));
}, []);
return (
<div>
{todos?.map((todo, i) => (
<div key={i} onClick={() => toggleTodo(i)}>
{todo.completed ? "✅" : "⬜"} {todo.text}
</div>
))}
<button onClick={() => addTodo("New task")}>Add</button>
</div>
);
}
Comments
import { Thread, Composer } from "@liveblocks/react-comments";
import { useThreads } from "./liveblocks.config";
function Comments() {
const { threads } = useThreads();
return (
<div>
{threads.map((thread) => (
<Thread key={thread.id} thread={thread} />
))}
<Composer />
</div>
);
}
Liveblocks vs Alternatives
| Feature | Liveblocks | PartyKit | Socket.IO | Firebase |
|---|---|---|---|---|
| Free MAU | 1,000 | Unlimited | Self-host | 100 |
| Presence | ✅ Built-in | Manual | Manual | Manual |
| CRDT Storage | ✅ | Y.js | ❌ | ❌ |
| Comments | ✅ | ❌ | ❌ | ❌ |
| Notifications | ✅ | ❌ | ❌ | ✅ FCM |
| React hooks | ✅ | ❌ | ❌ | ❌ |
Resources
- Liveblocks Documentation
- GitHub Repository
- Examples — 20+ starter templates
- Pricing — free up to 1K MAU
Building collaborative data tools? My Apify scraping tools extract web data at scale — combine with Liveblocks for real-time collaborative data analysis. Questions? Email spinov001@gmail.com
Top comments (0)