Інтеграція Liveblocks для real-time collaboration в мобільному додатку
Liveblocks — managed-інфраструктура для спільного редагування: WebSocket-сервер, CRDT-хранилище, presence, коментарі, сповіщення — все за хмарним API. У браузері інтеграція займає день. У React Native — вже цікавіше, тому що офіційні React-хуки (@liveblocks/react) працюють, але ряд нюансів вспливає тільки у prodacшені.
Що дає Liveblocks з коробки
Три ключові примітиви:
Storage — CRDT-хранилище на базі власної реалізації (не Y.js). LiveObject, LiveList, LiveMap — типизовані структури з автоматичним merge. Зміни реплікуються між усіма клієнтами у кімнаті (<100 мс у межах одного регіону).
Presence — еphemeral steyт користувача: позиція курсора, виділення, статус. Не персистується, зберігається тільки поки користувач online. Відмінно для відображення аватарів та активності в реальному часі.
Yjs-інтеграція — з версії 1.x Liveblocks підтримує Y.js через @liveblocks/yjs, що дозволяє використовувати Tiptap/Slate на вебе та той же документ у мобільному додатку.
Підключення у React Native
import { createClient } from '@liveblocks/client';
import { createRoomContext } from '@liveblocks/react';
const client = createClient({
authEndpoint: '/api/liveblocks-auth', // JWT через ваш бекенд
// або publicApiKey для прототипів
});
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 обгортає екран-редактор. Всередину нього useStorage дає іммутабельний снапшот CRDT-хранилища, useMutation — транзакційні мутації.
Проблема в RN: @liveblocks/client під капотом використовує fetch та WebSocket — обидва є у React Native. Але EventSource (SSE, потрібен для Liveblocks Notifications) відсутній у RN без polyfill. Пакет react-native-event-source або eventsource + глобальний polyfill вирішують це:
// index.js, до всього іншого
import EventSource from 'react-native-event-source';
global.EventSource = EventSource;
Presence у мобільному контексті
У браузері presence зазвичай — координати миші. У мобільному додатку це безглуздо. Типічні кейси:
-
Спільне редагування списку задач: presence =
{ focusedItemId: string | null }— підсвічуємо елемент, який зараз редагує інший користувач. -
Спільна дошка: presence =
{ x, y, tool: 'pen' | 'eraser' }— показуємо стилус іншого учасника. -
Документний редактор: presence =
{ selection: { anchor, focus } | null }— виділення у тексті.
useOthersConnectionIds + useOther дають реактивні дані про присутніх. Для оптимізації: useOthersMapped дозволяє вибрати тільки потрібне поле presence та не перерендериватися при зміні несв'язаних полів.
Offline-режим: чого у Liveblocks немає з коробки
Liveblocks не надає offline-персистентність на клієнті. При відсутності мережі зміни втрачаються. Для мобільного додатка критично.
Обхідне рішення — буфер мутацій у AsyncStorage:
const [pendingMutations, setPendingMutations] = useAtom(pendingMutationsAtom);
// При потері мережі
NetInfo.addEventListener(state => {
if (!state.isConnected) {
// зберігаємо поточний steyт локально
const snapshot = storage.toObject();
AsyncStorage.setItem('offline_snapshot', JSON.stringify(snapshot));
} else {
// при восстановленні — реплікуємо pending
pendingMutations.forEach(mutation => mutation());
setPendingMutations([]);
}
});
Це не справжній CRDT-offline — merge не гарантирован при одночасних змінах на кількох пристроях offline. Для повноцінного offline на Liveblocks потрібно комбінувати з Y.js через @liveblocks/yjs та кастомним локальним провайдером. Рішення складніше, але правильніше.
AppState та реконнект
iOS убиває WebSocket-з'єднання при переході у фон. Liveblocks SDK обробляє реконнект автоматично, але room.getStatus() після восстановлення проходить через цикл reconnecting → connected. Якщо ваш UI не реагує на статус кімнати — користувач буде бачити застарілий steyт.
const status = useStatus(); // 'initial' | 'connecting' | 'connected' | 'reconnecting'
if (status === 'reconnecting') {
return <ReconnectingBanner />;
}
Обробляйте reconnecting явно — особливо якщо додаток показує спільний список, який міг змінитися поки пристрій був offline.
Тарифікація та лімити
Liveblocks — платний сервіс з free-tirom (50 MAU, 1000 кімнат). Для prodacшену: Starter від $99/мес, Pro від $299/мес. Це managed-інфраструктура — ви не поднімаєте WebSocket-сервери, але залежите від їх SLA (99.9% на Pro).
Якщо вимоги включають self-hosted або data residency у конкретній юрисдикції — Liveblocks не підійде. В такому разі розглядайте Y.js + Hocuspocus або PartyKit на власній інфраструктурі.
Оцінка
Інтеграція Liveblocks у React Native (Storage + Presence, без offline): 2–4 тижні. З offline-буфером та Y.js-інтеграцією: 5–8 тижнів. Flutter — через @liveblocks/client у webview або custom Dart-клієнт з WebSocket: 4–7 тижнів.







