The case for local-first tools
Local-first software keeps your data on your own machine, works offline and treats sync as optional. Why we build desktop tools this way, and the real costs: backup, multiple devices and collaboration.
Local-first software is software where the main copy of your data lives on your own device. The app reads and writes to local storage, it works with no network at all, and any sync to a server or another device happens in the background, when it can. The cloud becomes a helper rather than the place your work actually lives. Most of the tools we build lean this way, and this post is about why, and about what it costs.
The term comes from a 2019 essay by the research lab Ink & Switch, which set out a list of ideals: fast, works offline, multi-device, collaborative, long-lived, private, and under the user's control. We find the list useful mainly because it is honest. Some of those ideals are easy on a single machine and very hard across several. The interesting design work is in the gap.
What changes when data lives on your machine
The hero drawing shows the difference in the plainest terms we could manage. In a cloud-first app, on the left, the data sits on a server. Every read and every write travels over the network. The app on your laptop is mostly a window onto someone else's computer. When the network is slow, the app is slow. When it is gone, the app waits, or shows a spinner, or quietly loses what you typed.
In a local-first app, on the right, the data is a file or a database on your disk. The app talks to it directly. A read is a few microseconds to a few milliseconds, not a round trip to a data centre. If there is a sync service at all, it sits off to the side, and the app does not care whether it is reachable right now.
That one change has knock-on effects that are easy to underestimate:
- Speed. Search, sort and filter can run over the whole data set instantly, because it is all right there. No pagination, no loading states for things you already own.
- Offline by default. Trains, planes, bad hotel Wi-Fi and a server outage on the other side of the world all stop mattering.
- Privacy by construction. Data that never leaves your machine cannot leak from someone else's server. You do not need to trust a privacy policy for things that are not sent anywhere.
- Longevity. If the company behind the app disappears, the file is still on your disk. That is a real risk with cloud tools, and it happens more often than anyone likes to admit.
Why our desktop apps lean this way
Most of our desktop apps work on things that are already local. A folder you want to preview in a browser. A disk image you want to write to a USB stick. Photos you want to describe and tag. For jobs like these, sending data to a server would add latency, a privacy question and a point of failure, and give nothing back.
So the default for us is simple. No account. No upload. The app does its work where the data already is. When a tool genuinely has to use the network, as a downloader does, or as Exif AI does when you pick a cloud vision model over its local one, the network use is the job itself, and we try to make it obvious what is being contacted.
There is a practical side too. Local apps are cheaper to run, because there is no server bill that grows with every user. That matters for a small studio. A tool that runs entirely on your machine does not need a subscription just to pay for hosting. It also means there is no server for us to secure, patch and keep awake at three in the morning.
Storage: boring formats win
If data is going to live on someone's disk for years, the format matters more than it does on a server you control. You cannot run a migration script on a million laptops at once. Whatever you write today has to be readable by whatever version of the app someone opens in five years.
Our preferences, roughly in order:
- Plain files where possible. Text, JSON, images in standard formats. A user can open them in another program, back them up with anything and read them without us.
- SQLite when you need queries. It is a single file, it is extremely well tested, and the SQLite project itself makes a good case for using it as an application file format. Keep a schema version in the database and write migrations that only ever move forward.
- Standard locations. Application Support on macOS, AppData on Windows, the XDG directories on Linux. Users and backup tools know where to look.
What we avoid is a proprietary binary blob that only one version of one app can read. It is the local equivalent of a walled garden.
The costs, honestly
Local-first is not free, and anyone who tells you it is has probably only built the single-device version. Here are the parts that are genuinely harder.
Backup is now the user's problem. A cloud app backs itself up, in theory. A local app puts the data on a disk that can die, be stolen or be dropped in a lake. The honest answer is to make the data easy to back up (plain files in known places, so Time Machine, File History or any backup tool just works) and to say so clearly. We would rather tell people where their data is than pretend it cannot be lost.
Multiple devices need sync, and sync is hard. The moment you want the same data on a laptop and a desktop, you have two copies that can change independently. If both are edited while offline, something has to decide what the merged result looks like. For a single user moving between machines, simple approaches often work well enough: last write wins per record, or keep both versions and ask. For anything more, you need real conflict handling.
Collaboration is the hardest case. Several people editing the same document at the same time, some of them offline, is where local-first stops being a style and becomes a research problem. This is where CRDTs come in.
A careful word on CRDTs
A CRDT, or conflict-free replicated data type, is a data structure designed so that copies edited separately can always be merged, in any order, and end up the same. Libraries such as Automerge and Yjs implement CRDTs for text, lists and maps, and they are the reason collaborative local-first apps are practical at all.
They are clever, and they are worth knowing about. They are also easy to oversell. A few caveats we keep in mind:
- CRDTs guarantee that every copy converges to the same state. They do not guarantee that the state is what anyone meant. If two people edit the same sentence in different ways, the merge is consistent but may read strangely.
- They keep history or metadata to make merging possible, and that has a cost in size and memory. Modern libraries are much better at this than early ones, but it does not disappear.
- Some rules cannot be expressed as a merge. "Only one person can book this room" needs a single authority somewhere. No clever data structure removes that.
For the tools we make today, most of which are single-user, we have not needed CRDTs. If we build something collaborative, we will reach for a well-maintained library rather than write our own, and we will still keep a plain export format so the data is not locked into the sync layer.
A test we use
When we design a new tool, we ask one question early: does this still work with Wi-Fi switched off? If the answer is no, we want a good reason. Sometimes there is one. A downloader needs the network. A web service needs a server. But surprisingly often the answer is "it could, we just did not design it that way", and that is the moment to change the design, before there is a server to justify.
For our own tools, and for the custom software we build for clients, that question has saved us from more accidental infrastructure than any architecture review. Try it on the next app you open. Turn the Wi-Fi off first and see which of them still let you work.