manick
/
Writing

Jan 18, 2025

Why Local-First Apps Are the Future

For the past decade, we've been told that everything belongs in the cloud. Your documents, your photos, your data—all of it should live on someone else's computer. But something is shifting.

The Problems with Cloud-First

The cloud-first approach has some fundamental issues:

  1. You're always dependent on a connection - No wifi? No work.
  2. Latency is unavoidable - Every action requires a round-trip to a server
  3. Your data isn't really yours - Companies can lock you out, change terms, or disappear
  4. Collaboration is an afterthought - Most cloud apps handle conflicts poorly

What is Local-First?

Local-first means your app works on your device first, with sync as an enhancement:

// Local-first: data lives on device, syncs when possible
const document = await localDB.get('doc-123');
document.title = 'Updated locally';
await localDB.put(document);
// Sync happens in background, doesn't block UI
sync.push(document);

Compare this to cloud-first:

// Cloud-first: every change requires network
const response = await fetch('/api/documents/123', {
  method: 'PATCH',
  body: JSON.stringify({ title: 'Updated' }),
});
// User waits for this to complete
if (!response.ok) throw new Error('Failed to save');

Real-World Examples

Some apps are already leading the way:

  • Linear - Feels instant because it syncs in the background
  • Figma - Real-time collaboration that works offline
  • Obsidian - Your notes are markdown files on your computer
  • Excalidraw - Drawings stored locally with optional sync

The Technical Foundation

Building local-first apps requires different tools:

CRDTs

Conflict-free Replicated Data Types handle merging changes from multiple devices:

import { Doc } from 'yjs';

const doc = new Doc();
const text = doc.getText('content');

// Changes from any device can be merged
text.insert(0, 'Hello from device A');
// Another device's changes merge automatically

Sync Engines

Tools like PowerSync, ElectricSQL, and Replicache handle the complexity of syncing:

import { PowerSyncDatabase } from '@powersync/web';

const db = new PowerSyncDatabase({
  schema: AppSchema,
  database: { dbFilename: 'app.db' },
});

// Write locally
await db.execute('INSERT INTO tasks (title) VALUES (?)', ['New task']);
// Sync happens automatically in background

Why This Matters Now

Several trends are converging:

  1. Better tooling - CRDTs and sync engines are maturing
  2. User expectations - People expect apps to work offline
  3. Privacy awareness - Users want control over their data
  4. Edge computing - Computing is moving closer to users

Getting Started

If you're building a new app, consider starting local-first:

  1. Store data in SQLite or IndexedDB on the client
  2. Use a sync engine to handle replication
  3. Design for offline-first, online-enhanced
  4. Handle conflicts gracefully with CRDTs

The future of software is local-first. The question is whether you'll build it or be disrupted by it.