We regularly receive requests to implement collaborative spreadsheet editing in a mobile app. A spreadsheet is not text. It has a structure: rows, columns, cells, formulas, and dependencies between cells. Synchronization algorithms that work for text documents (Y.js YText, CRDT for sequences) apply differently here. Concurrently editing cell B3 and simultaneously changing the formula =SUM(B1:B5) in C1 – these are two independent events with data dependency. We offer a turnkey solution using CRDT.
How to ensure data consistency during concurrent editing?
Data model: not an array of rows, but a Map of cells
A spreadsheet in CRDT representation is a Y.Map (or equivalent) where the key is a "row:col" string and the value is a cell object. Y.js YMap supports concurrent operations at the individual key level: two users edit different cells simultaneously – no conflict. Two edit the same cell – the last write wins (Last-Write-Wins semantics). This is confirmed by Y.js documentation.
const ycells = ydoc.getMap('cells'); ycells.set('3:2', { value: '=SUM(A1:A3)', formatted: '15', style: { bold: true } }); ycells.observe(event => { event.changes.keys.forEach((change, key) => { if (change.action === 'update' || change.action === 'add') { const [row, col] = key.split(':').map(Number); recalculateAffectedFormulas(row, col); rerenderCell(row, col); } }); }); Last-Write-Wins for cells is acceptable in most scenarios. For critical data (financial spreadsheets), a conflict detector and UI for manual resolution are needed.
Adding and deleting rows/columns: structural changes
This is more complex than a cell change. Inserting a row before row 5 should shift all references in formulas: =B5 becomes =B6. Concurrent row insertion by two users – the order of insertion affects the final structure.
Y.js YArray for a list of rows with YMap for each row is a classic scheme. When inserting into YArray, CRDT guarantees deterministic merge order (based on clientId and clock). Y.js with YMap handles simultaneous changes faster than OT solutions with operation logs, on average 2x faster, with the same reliability. Formulas after structural changes need to be recalculated via a dependency graph.
Formula recalculation is a separate topic. HyperFormula (hyperformula.js) – an open-source formula engine (similar to Excel), runs in JavaScript. Supports 400+ functions. Integrates with Y.js: when a CRDT update arrives, we apply it to HyperFormula via setSheetContent() and get recalculated values.
Which tools to choose for a mobile spreadsheet editor?
Mobile table rendering
FlashList (React Native) with 2D scrolling – a non-standard case. The standard FlatList / ScrollView is not optimized for large tables with frozen headers and cell virtualization. React Native with FlashList provides smoother scrolling than standard FlatList, roughly 2-3 times.
Solutions:
-
react-native-table-component– simple, no virtualization, not for large tables. -
react-native-spreadsheet– exists but hasn't been updated for a long time. - Custom renderer using
react-native-gesture-handler+react-native-reanimated– full control, manual virtualization.
On native Android: RecyclerView with GridLayoutManager, built-in virtualization. On iOS: UICollectionView with UICollectionViewCompositionalLayout for 2D scrolling with variable cell sizes.
| Platform | Component | Virtualization | Complexity |
|---|---|---|---|
| React Native | FlashList (custom) | Yes | High |
| Android | RecyclerView + GridLayoutManager | Built-in | Medium |
| iOS | UICollectionView + CompositionalLayout | Built-in | Medium |
Custom cell editor: TextInput with formula support (starts with = – switch mode). On iOS autocorrect breaks formulas (=SUM becomes =Sum or gets underlined). Need autocorrectionType = .no and spellCheckingType = .no for cells in formula mode.
Range selection: B2:D5
When entering a formula, the user should tap a cell and drag to select a range – classic Excel UX. On mobile: a custom gesture recognizer over the table that tracks pan gestures and calculates the cell range from coordinates. Need hitTest to determine the cell from the touch point.
Multiple users can select ranges simultaneously – show using Awareness Protocol (like cursors in a text editor), but with color coding per user.
Synchronization via WebSocket: optimistic updates
A local cell change is applied to the UI immediately (optimistic update). In parallel, Y.js encodes the operation into an update binary and sends it to the server. The server broadcasts the update to other clients. If a conflicting update arrives – Y.js merge applies, UI rerenders.
For large tables (1000+ rows), it's important not to rerender the entire table on each remote update. We track event.changes.keys in the observer and rerender only the changed cells plus dependent formulas.
What the work includes
- Architecture and prototype: stack selection, data schema, CRDT structures
- Integration of Y.js + HyperFormula with native modules
- Implementation of a custom renderer with virtualization
- WebSocket server setup and traffic optimization
- Load testing (up to 50 concurrent users)
- Documentation and team training
- Post-deployment support (1 month)
Our experience includes developing over 10 mobile apps with CRDT synchronization. We have 5+ years of mobile development experience and guarantee deadline adherence.
Estimation
MVP with Y.js + HyperFormula on React Native (up to 100 rows, basic formulas, 2–3 concurrent users) – 10–16 weeks. A full-fledged Google Sheets-like mobile editor with thousands of rows, complex formulas, and structural changes – 6–12 months.
Contact us for a project evaluation. Get a consultation on architecture and timeline – it takes no more than 2 working days.







