Local-First Architecture: CRDTs and the End of Spinning Spinners
In 2025, the honeymoon with purely cloud-based applications is over. We've all experienced the frustration: an app that takes three seconds to load a list, or one that just stops working when your Wi-Fi drops. We are finally building local-first.
In a local-first application, the primary source of truth is on the user's device (often in an embedded SQLite or indexedDB database), and the cloud is just a synchronization and backup layer.
The Problem: Conflict Resolution
How do you sync data between devices without a central server deciding what's right? If two people edit the same document offline and then sync, how do you merge their changes? This is where CRDTs (Conflict-free Replicated Data Types) come in.
CRDTs are data structures that can be updated independently and concurrently, and they always merge back together into a consistent state.
Practical Example: Collaborative Notes with Yjs
In 2025, libraries like Yjs and Automerge are the backbone of most local-first apps. Here's a conceptual look at how you'd use them:
import * as Y from 'yjs';
import { IndexeddbPersistence } from 'y-indexeddb';
import { WebrtcProvider } from 'y-webrtc';
const doc = new Y.Doc();
// 1. Local Persistence: Works immediately, even without internet
const persistence = new IndexeddbPersistence('notes-db', doc);
// 2. Peer-to-Peer Sync: Low latency updates
const provider = new WebrtcProvider('notes-room', doc);
const yNotes = doc.getArray('notes');
// Adding a note is instantaneous
function addNote(text) {
yNotes.push([{ content: text, timestamp: Date.now() }]);
}
yNotes.observe((event) => {
console.log('Notes updated across devices!', yNotes.toArray());
});
The Tooling Revolution: SQLite in the Browser
The other half of the 2025 local-first revolution is SQLite over Wasm. With libraries like wa-sqlite and electric-sql, we can run a full relational database in the browser with performance that rivals native apps.
-- Your client-side code just runs SQL
SELECT * FROM tasks WHERE status = 'pending' ORDER BY priority DESC;
Then, the sync layer (like ElectricSQL or Replicache) handles pushing and pulling changes to the backend in the background.
Why It's Better
- Instant Response: No more loading spinners. Every interaction is local.
- Offline-Capable: The app is fully functional on a plane, a train, or in a basement.
- Data Privacy: Users can own their data, potentially storing it on their own storage provider.
In 2025, local-first isn't just a niche idea-it's how the best apps are built. We've finally stopped treating the internet connection as a constant and started treating it as an occasional luxury.
Aunimeda designs and builds scalable software architectures - from system design to implementation and ongoing engineering.
Contact us to discuss architecture for your project. See also: Custom Software Development, Web Development