Hey everyone! I'm working on adding local-first features to a web framework, and I'm really interested in your insights, especially if you've used local-first or sync engine solutions.
When I mention "Local-First," I'm referring to an architecture where the user interface isn't reliant on network connectivity. It means:
- **Instant UI** – Accessing data from a local store without waiting for server responses.
- **Optimistic updates** – Changes are applied instantly to the local state and synced with the server later.
- **Offline resilience** – The app continues functioning without connectivity.
- **The server's role** – It manages things like authentication, conflict resolution, and shared state while being the authoritative data source.
Now, I'd love to hear about your ideal developer experience! Think beyond technical limitations—what would you **want** this to look like if there were **no constraints**?
To guide your thoughts, here are some areas to consider:
1. **Declarative sync** - How should a developer declare what data is available locally and how it syncs?
2. **Conflict resolution** - What should happen when changes conflict among users or between a user and the server? Should it be automatic, configurable, or something else?
3. **Offline experience** - What should the app's behavior be when it goes offline and then reconnects? Where should the framework take charge versus giving developers control?
4. **Inspiration from other tools** – If you've used any local-first or sync solutions that provided a great developer experience, what did you find useful?
I'm all ears for bold ideas—don't hesitate to share even if something seems far-fetched. Looking forward to our conversation! 🙂
5 Answers
For the dream developer experience, I imagine having a system where you can annotate your models to specify what syncs and how. Like, you could mark fields and queries, and the framework would automatically generate a local storage and syncing layer that ensures UI reads are local and instant, even while syncing runs in the background.
When it comes to conflict resolution, I'd prefer automatic CRDT merges for most fields, with exceptions for specific scenarios (like using last-write-wins for larger blobs). Plus, giving developers custom merge hooks for certain fields would make life a lot easier.
For offline UX, I'd implement a system to persist operations with resumable IDs and a way to prioritize and queue actions. Having a Sync Inspector tool would also help during debugging—it's crucial for understanding pending actions and reducing user confusion.
This is such a pertinent discussion! I think the ideal setup would allow conflict resolution to be seamless by default but enable developers to tap into more granular controls when necessary. Most sync engines usually offer one or the other.
Imagine defining your schema once, achieving immediate local reads with optimistic writing, and having syncing work effortlessly until something goes awry. From there, clear escape routes should be evident rather than hidden.
I'm all about keeping things simple for conflict resolution. I want it to operate without me having to pick versions manually every time. The idea of CRDTs is promising, but I'll admit I want it to happen without my interference.
Understood! The less visual complexity during conflicts, the better.
Conflict resolution can definitely get tricky. In my experience, I found that relying on last-write-wins work best for most data, but having an option for developers to attach custom merge functions when specific business logic is in play is crucial.
I also think the Sync Inspector suggestion really stands out. When conflicts arise or if users have a backlog of operations to replay, having visibility becomes vital—most libraries neglect this aspect.
Additionally, simplifying subscription management, like limiting users to their respective workspaces without going through convoluted auth processes, would enhance the experience a lot.
I see what you're getting at! That clearer scoping for subscriptions would definitely save time and headaches for developers.
I'm working on something similar, and this topic has definitely kept me up at night. The key aspect is having conflict resolution that works invisibly by default but allows for precise control when necessary—most sync solutions skew towards either extreme.

Thanks for the insight! I completely agree on the model annotation approach to avoid any coupling with the UI. It keeps the syncing process tied to the data itself, which is much cleaner.