Why local-first is the right default for workspace apps

Reads should never hit the network. Here's the case for IndexedDB as primary storage and why optimistic updates alone aren't enough.

Why local-first is the right default for workspace apps

Most workspace apps treat the network as the primary storage layer. When you open a document, it fetches from a server. When you type, it sends a request. When you go offline, it breaks.

Local-first inverts this. The primary storage is on the client — IndexedDB, in our case. Every read is instant because it never touches the network. Every write is optimistic because it doesn't wait for a server response.

The problem with network-first

Network-first apps feel fast on a good connection and broken on a bad one. The common patch is optimistic updates: show the result before the server confirms it. But optimistic updates without rollback are a lie — if the server rejects the mutation, the UI is wrong and the user doesn't know it.

Local-first with server authority

The right model is local-first reads with server authority. Reads come from the local replica. Writes go to the local replica first (for instant UI), then queue for the server (for persistence). If the server rejects a write, roll back the local replica to the pre-write snapshot.

Why IndexedDB?

IndexedDB is the only persistent storage available in all modern browsers. It supports transactions, indices, and large datasets. Dexie.js wraps it with a clean API and TypeScript support.

The tradeoff: IndexedDB is not as fast as an in-memory store. We mitigate this by keeping a Zustand layer on top — reads go to Zustand, which is hydrated from IndexedDB on startup and kept in sync with every write.