Два користувачі одночасно редагують список завдань: один на iPhone, інший на Android. Дані швидко розходяться, конфлікти накопичуються, і підсумковий стан непередбачуваний. REST API не забезпечує real-time, а створення власного WebSocket-сервера з CRDT вимагає місяців розробки та налагодження. Втрати даних через конфлікти можуть сягати 15% від усіх змін.
Ми використовуємо Liveblocks — керовану інфраструктуру для спільного редагування. Вона надає готовий WebSocket-сервер, CRDT-сховище (на базі Liveblocks Storage), presence, коментарі та нотифікації через хмарне API. У браузері інтеграція займає один день, у мобільному застосунку на React Native — від тижня. Це прискорює вихід на ринок у 3–6 разів порівняно з самостійною реалізацією. Економія на інфраструктурі може бути значною.
Що дає Liveblocks з коробки?
- Storage — CRDT-сховище на базі власної реалізації з
LiveObject,LiveList,LiveMap. Зміни реплікуються між клієнтами за <100 мс в одному регіоні. - Presence — ефемерний стан користувача: позиція курсора, виділення, статус. Не персистується, тільки онлайн.
- Yjs-інтеграція — через
@liveblocks/yjsможна використовувати Yjs документи спільно з веб-редакторами (Tiptap, Slate).
Згідно з Wikipedia, CRDT гарантує консистентність у розподілених системах.
Як підключити Liveblocks у React Native?
- Встановіть пакети:
npm install @liveblocks/client @liveblocks/react - Створіть клієнт за допомогою
createClientта налаштуйте аутентифікацію. - Визначте типи Presence та Storage для вашого застосунку.
- Експортуйте хуки через
createRoomContext. - Оберніть екран редактора в
RoomProvider.
import { createClient } from '@liveblocks/client'; import { createRoomContext } from '@liveblocks/react'; const client = createClient({ authEndpoint: '/api/liveblocks-auth', // або 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 дає імутабельний снапшот, useMutation — транзакційні мутації.
Проблема в RN: @liveblocks/client використовує fetch і WebSocket, які є, але EventSource (SSE для нотифікацій) відсутній без поліфіла. Встановіть react-native-event-source та глобальний поліфіл:
import EventSource from 'react-native-event-source'; global.EventSource = EventSource; Отримайте консультацію щодо налаштування клієнта та поліфілів — це зекономить вам до 40% часу на інтеграцію.
Що таке Presence у мобільному контексті?
У браузері presence — координати миші. У мобільному застосунку — інші кейси:
- Спільне редагування списку завдань: presence =
{ focusedItemId: string | null }— підсвічуємо редагований елемент. - Спільна дошка: presence =
{ x, y, tool: 'pen'|'eraser' }— показуємо стилус. - Документний редактор: presence =
{ selection: { anchor, focus } | null }— виділення тексту.
Використовуйте useOthersConnectionIds та useOther для реактивних даних про присутніх. Для оптимізації — useOthersMapped вибирає тільки потрібні поля.
Чому варто обрати Liveblocks для мобільного застосунку?
| Критерій | Liveblocks | Самостійна реалізація |
|---|---|---|
| Час до продакшену | 2–8 тижнів | 3–6 місяців |
| Підтримка офлайну | Вимагає доопрацювання | Повний контроль |
| Вартість інфраструктури | Підписка | Значні витрати |
| SLA | 99.9% (Pro) | Залежить від вас |
| Data residency | Тільки хмара | Повний контроль |
Порівняння показує: Liveblocks виграє за швидкістю інтеграції, але поступається в офлайн-режимі та контролі даних. Для більшості MVP та середніх проєктів цього достатньо.
Офлайн-режим: чого немає з коробки
Liveblocks не надає офлайн-персистентність. При відсутності мережі зміни втрачаються. Ми вирішуємо це буфером мутацій в AsyncStorage:
Приклад реалізації офлайн-буфера
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([]); } }); Це не справжній CRDT-офлайн — merge не гарантований при одночасних змінах офлайн. Для повного рішення комбінуємо з Yjs через @liveblocks/yjs та кастомним локальним провайдером. Складніше, але правильніше.
| Стратегія офлайну | Складність | Гарантія консистентності | Час впровадження |
|---|---|---|---|
| Буфер мутацій | Низька | Ні, можлива втрата | 1–2 тижні |
| Локальна копія + merge | Середня | Часткова (last-write-wins) | 2–4 тижні |
| Yjs з локальним провайдером | Висока | Повна CRDT | 4–8 тижнів |
AppState та реконект
iOS вбиває WebSocket при фоні. SDK перепідключається автоматично, але room.getStatus() проходить через reconnecting. Якщо UI не реагує, користувач бачить застарілий стан. Обробляйте явно:
const status = useStatus(); if (status === 'reconnecting') { return <ReconnectingBanner />; } Що входить в роботу
- Налаштування клієнта Liveblocks та провайдера аутентифікації.
- Інтеграція Storage та Presence для вашого use-case.
- Реалізація офлайн-буфера (опціонально).
- Yjs-інтеграція для сумісності з веб-редакторами.
- Тестування реконекту та обробки appState.
- Документація та навчання команди.
- Підтримка 2 тижні після деплою.
Замовте демо-проєкт з базовою інтеграцією — ми покажемо, як Liveblocks працює у вашому контексті. Зв'яжіться з нами для оцінки проєкту за 1–2 робочі дні.
Як ми оцінюємо проєкт
Орієнтовні строки:
- Базова інтеграція (Storage + Presence, без офлайну): 2–4 тижні.
- З офлайн-буфером: 3–6 тижнів.
- З Yjs та повним офлайном: 6–10 тижнів.
Вартість розраховується індивідуально після аналізу вашого MVP. Отримайте консультацію — оцінимо проєкт за 1–2 робочі дні.
Наш досвід: понад 5 років у мобільній розробці, 15+ проєктів з real-time синхронізацією. Працюємо з Liveblocks з моменту його публічного релізу.







