bartblast
Designing Local-First features for Hologram - what's your dream DX?
Hi everyone!
I’m working on designing Local-First features for Hologram and I’d love to hear your thoughts before I dive into implementation.
What do we mean by “Local-First”?
The term gets used in different ways, so let me clarify what I mean here. Local-First is an architecture pattern where the UI isn’t blocked by the network. In practice this means:
-
Instant UI - reads come from a local store, so there’s no spinner waiting for a server round-trip
-
Optimistic updates - writes apply immediately to the local state and sync to the server in the background
-
Offline resilience - the app keeps working when connectivity drops
-
The server still matters - it handles auth, conflict resolution, shared state, and remains the source of truth
It’s not about forcing all data to live on the client - it’s about making the user experience fast and resilient regardless of network conditions. How much data you keep locally is a design decision, not a requirement.
What I’m looking for
I’m in the early design phase and I’m genuinely open to ideas. I want to hear about your ideal developer experience - not what you think is technically feasible, but what you’d want it to look like if there were no constraints.
Some things I’ve been thinking about:
1. Declarative sync
How would you ideally declare which data should be available locally and how it syncs?
2. Conflict resolution
When two users (or a user and the server) make conflicting changes, what should that look like from the developer’s perspective? Should it be automatic, configurable, or something else?
3. Offline experience
What should happen when the app goes offline and comes back? How much of this should the framework handle transparently vs. giving the developer control?
4. Inspiration from other tools
Have you used any local-first or sync engine solutions (in any ecosystem - Zero, Electric SQL, PowerSync, Automerge, LiveStore, etc.) that had a great DX? What made it great?
Don’t hold back - the bolder the idea, the better. Even if something seems unrealistic, it might spark a direction I haven’t considered.
Looking forward to the discussion! ![]()
Trending in Discussions
Other Trending Topics
Latest Hologram Threads
Chat & Discussions>Discussions
Latest on Elixir Forum
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #hex
- #performance










First 10 of 32 Posts
LostKobrakai
I’d suggest you to look at tanstack db. They’re kinda building what you’re describing for the javascript ecosystem.
bartblast
TanStack DB is definitely on my radar - they’re doing some very interesting work there!
derek-zhou
I’d say neither the framework nor the application developer should handle offline events, because “offline” is hard to define. Websocket broke? No heartbeat received in last XX seconds? All have their caveats, and in the case of mobile internet, the server can be flooded with such events. Because of this, I’d advocate against of relying on websocket, but that’s probably not what you want to hear. You did say don’t hold back though.
jam
In general, I think the dream DX is just write queries and mutations and Hologram makes sure the data stays in sync. Ideally these would be no different than how you’d usually write queries and mutations in Elixir and you’d opt out of optimistic updates if needed. Hologram would also handle prefetching queries to help everything feel instant.
I think Zero, Instant, and Convex have the philosophy of “it’s just queries and mutations”. They all have their own query language to achieve it though, and for the latter two, I think they own the db as well. Meteor.js also had a similar philosophy and allowed you to write Mongo queries on server and client with some small differences. Zero improves upon this by letting you write the query once even though I don’t love their custom syntax. For Hologram, I think a similar DX to Zero but “it’s just standard Elixir” could be something to aspire to. I’m not sure that their SQLite cache that sits between Postgres and the client would be required to pull it off. The issues I see with the other attempts are either:
Now to keep things a bit more manageable, I think Hologram should focus on supporting Last Write Wins to start and maybe as it’s only reconciliation technique. Otherwise, I think complexity explodes in what is already fairly complex.
For offline, queue the writes and replay when back online. The replays go through the same mutation business logic so if something has changed that would prevent their write, it’ll just get rolled back. There should also probably be a config to determine when a full sync is required, e.g. after X days offline with a default that won’t get people into too much trouble, maybe 1 day.
Like Zero, I think you could constrain the types of supported queries in a first release and potentially expand over time. For example, I think Zero still doesn’t support aggregates.
esambo
Local first would be awesome for LiveView Native, as native app users are used to instant responsiveness.
jam
As an aside, it would be amazing if one day you could pair Hologram and @garrison’s Hobbes if the stars align.
esambo
Some iOS apps offer a sync feature that makes this local first concept more explicit, where the apps is first and foremost a local app, and sync is secondary. Some apps that come to mind are:
And then there are web apps that offer an offline mode:
Git as probably also a good example of different merge conflict strategies, and 3-way-merge.
bartblast
You’re right that “offline” is hard to pin down. I think the sync engine still needs some form of connectivity awareness at the framework level though. If a sync attempt fails, the framework needs to know when to retry. Whether that’s periodic retries with backoff, reacting to the connection being re-established, or some combination - I’m not sure we can completely avoid the problem you’re describing. With a declarative data model this was always going to be the framework’s responsibility, not the developer’s - but you’re right that it’s a hard problem to solve well.
By the way, Hologram doesn’t use WebSockets - it communicates via standard HTTP requests with a keep-alive mechanism. But the challenge applies regardless of transport.
What approach would you lean toward?
derek-zhou
I agree with you; the client side needs to be aware of the connectivity situation. On the other hand, the server side should refrain from doing anything special when a client is perceived “offline”.
I was under the impression that Hologram use websocket, just not Phoenix Socket. Maybe I was hallucinating.
bartblast
Thanks @jam, really appreciate the detailed thoughts!
I agree with a lot here, especially the “make it invisible” direction - that’s exactly the bar I’m aiming for. Code should work the same way on both client and server, and developers shouldn’t need to think about what’s local vs. remote.
One place I’m thinking differently though is query-based sync. Syncing queries couples the sync layer to the UI - every time a component changes what it displays, you’re renegotiating what lives on the client. I’d rather sync underlying data based on declarative authorization rules. You define once what data a user/role gets locally (you need those rules for security anyway), and then components just query freely against whatever’s available. That’s where the real “just write queries” DX comes from I think - not because queries drive the sync, but because sync is already handled underneath.
On the query side - I definitely don’t want to invent a new language. But I’d like to explore some ideas from Gel (previously EdgeDB), their composability is way ahead of SQL. Probably more of a query API than a language, something that feels natural in Elixir.
As for event sourcing - I’m not ruling it out. Maybe there’s a lightweight version that collapses to simple atomic operations in most cases, giving you the “what happened” benefits without the usual complexity. Still weighing that against going straight to LWW.
And yeah, fully agree on starting constrained. Ship a small subset of the most needed operations first, validate the DX, expand from there.
Great discussion, keep the ideas coming!