Liveblocks gives you real-time collaboration primitives: live cursors, presence, conflict-free storage, and comments. Build multiplayer experiences like Figma, Notion, and Linear.
What Is Liveblocks?
Liveblocks provides the infrastructure for collaborative features. Instead of building WebSocket servers and conflict resolution from scratch, use Liveblocks APIs.
Quick Start
npm install @liveblocks/client @liveblocks/react
Setup
// liveblocks.config.ts
import { createClient } from '@liveblocks/client'
import { createRoomContext } from '@liveblocks/react'
const client = createClient({ publicApiKey: process.env.NEXT_PUBLIC_LIVEBLOCKS_KEY! })
export const { RoomProvider, useMyPresence, useOthers, useStorage, useMutation } = createRoomContext(client)
Live Cursors
function Cursors() {
const others = useOthers()
const [myPresence, updateMyPresence] = useMyPresence()
return (
<div onPointerMove={(e) => updateMyPresence({ cursor: { x: e.clientX, y: e.clientY } })}>
{others.map(({ connectionId, presence }) => (
presence.cursor && (
<div key={connectionId} style={{ position: 'fixed', left: presence.cursor.x, top: presence.cursor.y }}>
<svg width="24" height="24"><path d="M0 0l8 20 4-8 8-4z" fill="#e57373" /></svg>
</div>
)
))}
</div>
)
}
Conflict-Free Storage
function TodoList() {
const todos = useStorage((root) => root.todos)
const addTodo = useMutation(({ storage }, text: string) => {
const todos = storage.get('todos')
todos.push({ text, done: false, id: crypto.randomUUID() })
}, [])
const toggleTodo = useMutation(({ storage }, id: string) => {
const todos = storage.get('todos')
const todo = todos.find((t) => t.get('id') === id)
todo?.set('done', !todo.get('done'))
}, [])
return (
<div>
<button onClick={() => addTodo('New task')}>Add</button>
{todos?.map((todo) => (
<div key={todo.id} onClick={() => toggleTodo(todo.id)}>
{todo.done ? '✅' : '⬜'} {todo.text}
</div>
))}
</div>
)
}
Comments
import { Thread, Composer } from '@liveblocks/react-comments'
import '@liveblocks/react-comments/styles.css'
function CommentsPanel() {
const { threads } = useThreads()
return (
<div>
{threads.map((thread) => <Thread key={thread.id} thread={thread} />)}
<Composer />
</div>
)
}
REST API
export LB_KEY="your-secret-key"
# Get room storage
curl -s 'https://api.liveblocks.io/v2/rooms/my-room/storage' \
-H "Authorization: Bearer $LB_KEY"
# Get active users
curl -s 'https://api.liveblocks.io/v2/rooms/my-room/active_users' \
-H "Authorization: Bearer $LB_KEY" | jq 'length'
# Initialize storage
curl -s -X POST 'https://api.liveblocks.io/v2/rooms/my-room/storage' \
-H "Authorization: Bearer $LB_KEY" \
-H 'Content-Type: application/json' \
-d '{"liveblocksType": "LiveObject", "data": {"todos": {"liveblocksType": "LiveList", "data": []}}}'
Free Tier
| Feature | Free | Starter ($25/mo) |
|---|---|---|
| MAU | 250 | 10,000 |
| Connections | 20 concurrent | 200 |
| Storage | 50 rooms | 500 rooms |
| Comments | Yes | Yes |
| Presence | Yes | Yes |
Building collaborative data tools? Scrapfly provides shared web data. Email spinov001@gmail.com for collaborative scraping solutions.
Top comments (0)