Two users simultaneously edit a task list: one on an iPhone, the other on an Android. Data diverges quickly, conflicts pile up, and the final state is unpredictable. REST API doesn't provide real-time, and building a custom WebSocket server with CRDT takes months of development and debugging. Data loss due to conflicts can reach 15% of all changes.
We integrate Liveblocks—a managed infrastructure for collaborative editing. It provides a ready-made WebSocket server, CRDT storage (based on Liveblocks Storage), presence, comments, and notifications via a cloud API. In the browser, integration takes a day; in a React Native mobile app, from a week. This accelerates time to market by 3–6 times compared to building from scratch. Infrastructure cost savings can be significant.
What Liveblocks gives out of the box?
- Storage — CRDT storage based on its own implementation with LiveObject, LiveList, LiveMap. Changes replicate between clients in <100 ms within the same region.
- Presence — ephemeral user state: cursor position, selection, status. Not persisted, only online.
- Yjs integration — via @liveblocks/yjs you can use Yjs documents with web editors (Tiptap, Slate).
According to Wikipedia, CRDT guarantees consistency in distributed systems.
How to connect Liveblocks in React Native?
- Install packages:
npm install @liveblocks/client @liveblocks/react - Create a client with
createClientand configure authentication. - Define Presence and Storage types for your app.
- Export hooks via
createRoomContext. - Wrap the editor screen in RoomProvider.
import { createClient } from '@liveblocks/client'; import { createRoomContext } from '@liveblocks/react'; const client = createClient({ authEndpoint: '/api/liveblocks-auth', // or publicApiKey for prototypes }); type Presence = { cursor: { x: number; y: number } | null; selectedItemId: string | null; }; type Storage = { items: LiveList<{ id: string; text: string; done: boolean }>; }; export const { RoomProvider, useMyPresence, useStorage, useMutation } = createRoomContext<Presence, Storage>(client); RoomProvider wraps the editor screen. Inside, useStorage gives an immutable snapshot, useMutation — transactional mutations.
Issue in RN: @liveblocks/client uses fetch and WebSocket, which are available, but EventSource (SSE for notifications) is missing without a polyfill. Install react-native-event-source and a global polyfill:
import EventSource from 'react-native-event-source'; global.EventSource = EventSource; Get a consultation on client setup and polyfills—this saves you up to 40% of integration time.
What is Presence in a mobile context?
In a browser, presence means mouse coordinates. In a mobile app, other use cases:
- Collaborative task list editing: presence =
{ focusedItemId: string | null }— highlight the item being edited. - Collaborative whiteboard: presence =
{ x, y, tool: 'pen'|'eraser' }— show the stylus. - Document editor: presence =
{ selection: { anchor, focus } | null }— text selection.
Use useOthersConnectionIds and useOther for reactive presence data. For optimization, useOthersMapped selects only necessary fields.
Why choose Liveblocks for a mobile app?
| Criterion | Liveblocks | Self-built implementation |
|---|---|---|
| Time to production | 2–8 weeks | 3–6 months |
| Offline support | Needs work | Full control |
| Infrastructure cost | Subscription | Significant |
| SLA | 99.9% (Pro) | Depends on you |
| Data residency | Cloud only | Full control |
The comparison shows: Liveblocks wins on integration speed but falls short on offline mode and data control. For most MVPs and medium projects, this is sufficient.
Offline mode: what is not included out of the box
Liveblocks does not provide offline persistence. When the network is absent, changes are lost. We solve this with a mutation buffer in AsyncStorage:
Example offline buffer implementation
const [pendingMutations, setPendingMutations] = useAtom(pendingMutationsAtom); NetInfo.addEventListener(state => { if (!state.isConnected) { const snapshot = storage.toObject(); AsyncStorage.setItem('offline_snapshot', JSON.stringify(snapshot)); } else { pendingMutations.forEach(mutation => mutation()); setPendingMutations([]); } }); This is not true CRDT offline—merge is not guaranteed with concurrent offline changes. For a full solution, combine with Yjs via @liveblocks/yjs and a custom local provider. More complex but correct.
| Offline strategy | Complexity | Consistency guarantee | Implementation time |
|---|---|---|---|
| Mutation buffer | Low | No, possible loss | 1–2 weeks |
| Local copy + merge | Medium | Partial (last-write-wins) | 2–4 weeks |
| Yjs with local provider | High | Full CRDT | 4–8 weeks |
AppState and reconnect
iOS kills WebSocket when in background. The SDK auto-reconnects, but room.getStatus() goes through 'reconnecting'. If the UI doesn't react, the user sees stale state. Handle it explicitly:
const status = useStatus(); if (status === 'reconnecting') { return <ReconnectingBanner />; } What's included in the work
- Setting up Liveblocks client and authentication provider.
- Integrating Storage and Presence for your use case.
- Implementing an offline buffer (optional).
- Yjs integration for compatibility with web editors.
- Testing reconnect and appState handling.
- Documentation and team training.
- 2 weeks of support after deployment.
Order a demo project with basic integration—we'll show how Liveblocks works in your context. Contact us for a project estimate within 1–2 business days.
How we estimate the project
Indicative timelines:
- Basic integration (Storage + Presence, no offline): 2–4 weeks.
- With offline buffer: 3–6 weeks.
- With Yjs and full offline: 6–10 weeks.
Cost is calculated individually after analyzing your MVP. Get a consultation—we'll estimate the project within 1–2 business days.
Our experience: more than 5 years in mobile development, 15+ projects with real-time synchronization. We have worked with Liveblocks since its public release.







