The future of frontend apps: from fetch-and-hope to sync engines

Marcus Webb

Marcus Webb

August 28, 2026

The future of frontend apps: from fetch-and-hope to sync engines

I spent a decade teaching UIs to fetch. Mount, request, set state, hope the server still agrees. Spinner if it does not. Optimistic update if we felt brave, then a rollback that looked like a bug. That pattern built the web I shipped. It is also why a notes app still feels worse than a native one on a train, and why two people editing a list still takes a meeting to design. The future I am actually betting on is not a new framework. It is a different source of truth: a local store that syncs, instead of a remote JSON blob we poll and pray about.

I do not mean every marketing site needs a CRDT. I mean the apps we call products — the ones people open every day, on more than one device, sometimes offline — are moving toward sync engines whether we name them or not. Fetch-and-hope will stay for the brochure. The brochure is not the future of the job.

What fetch-and-hope actually was

It was a deal. The server owns truth. The client is a view. HTTP is the conversation. Caching is a performance trick we apologize for. That deal is honest when the UI is a report. It is a lie when the user has already typed and the network is a tunnel. We papered the lie with skeletons, retries, and “failed to save” toasts. Users learned to hate the toast. We learned to add more retries.

React Query and SWR made the deal kinder. They did not change the deal. They made fetch less embarrassing. I still use them. I will keep using them for admin pages that are a table plus a form. I will not use them as a philosophy for an app that has to work in a basement and a plane.

Phone spinner in a subway carriage, commuter waiting on a fetch

What broke the deal

Phones. Offline is not an edge. It is Tuesday. Multiple tabs. Multiple devices. Collaboration that is not a Google doc but still two people. AI features that want to read local context without a round trip. Users who have felt Linear or Figma and then open our CRUD and feel the 2016.

Also: we got good at optimistic UI and then we lied. The button said saved. The server said 409. The next fetch overwrote the user’s sentence. Fetch-and-hope does not have a merge story. It has a last-write story we pretend is fine. It is fine until it is a customer’s note.

Backend-for-frontend helped the shape of the JSON. It did not help the plane. The plane does not have your BFF. The plane has a cache if you built one, or a blank screen if you did not.

What I mean by a sync engine

A local database the UI reads and writes first. A protocol that moves changes, not whole pages. Conflict rules you chose on purpose. A server that is a participant, not the only adult in the room. Sometimes that is a CRDT. Sometimes it is a replication layer over Postgres. Sometimes it is a product you buy so you do not invent a research paper. The shape is: the client is allowed to be ahead, and catching up is the product, not an error toast.

This is older than the current names. Offline-first people said it. Couch people said it. Native people lived it. The frontend mainstream is late because we had HTTP and it was enough for dashboards. Dashboards are not where the attention is going.

Laptop in airplane mode still showing a working local-first app

What this does to the frontend job

State management gets less about which store library and more about which facts are local, which are ephemeral, and which must round-trip. You will still have UI state. You will have fewer “server state” caches that pretend they are not a second database.

You will think about permissions in the sync path, not only in the GET. A local store that contains another tenant’s row is not a clever cache. It is an incident. Fetch-and-hope leaked this too, in service workers we forgot. Sync engines make the leak obvious. Obvious is better. It is also work.

You will become friends with conflict. Not as a CS puzzle. As a product sentence: last writer, field merge, one person wins, or we show two versions. Fetch-and-hope hid conflict in a refresh. Users felt it as a ghost. Ghosts are worse than a dialog.

Framework wars matter less here than people want. A sync engine will sit under React or something else. The bet is the data path. I would rather be late on a framework and early on a truth model than the reverse. I have done the reverse. We rewrote the view and the train still spun.

What I am not betting on

Every app as a local-first religion. A bank export can stay fetch. A settings page can stay fetch. A CMS preview can stay fetch. Sync engines are a tax: schema on the client, migrations in two places, debugging a queue. I will not pay it so a landing page can work offline. I will pay it when the user is the writer, not only the reader.

I am not betting that “just use a bigger cache” is the future. Caches without a merge story are fetch-and-hope with a longer memory. Longer memory means staler lies.

I am not betting we all invent CRDTs. Most teams should buy or adopt a path that already has one. Inventing is how you get a science project and a support rota of one.

What the server becomes

Not a JSON vending machine. An authority for identity, for “this write is allowed,” for the merge when two locals disagree, for the audit when legal asks who changed the row. Fetch-and-hope treated the server as the only store. Sync treats it as the adult in a group of stores. That is a backend change too. If your API team only knows request-response, you will fight them. I have. The fight is worth having when the product is a writer. It is not worth having so a dashboard can feel trendy.

Auth tokens and sync are a sharp pair. A long-lived local store plus a dead token is a user who thinks they are saved and a server that will not take the save. The future includes a boring session story. I will not skip it because the engine demo used a magic key.

I also expect more “partial sync.” You do not replicate the warehouse to a phone. You replicate the user’s slice. Slice design is the new endpoint design. Get it wrong and you have a large local database and a slow first open. Get it right and the train works.

How I talk about it in 2026

I ask: is the user primarily reading or writing. Is offline a real hour of their week. Do two devices or two people meet on the same facts. If I get two yeses, I start the design from local-first, even if v1 still has a spinner. The spinner becomes a sync badge, not the whole architecture. If I get zeroes, I fetch, I cache politely, I go home.

The future of frontend apps, for the ones that feel like software instead of documents, is a store that lives next to the UI and a server that reconciles. Fetch-and-hope remains the default for pages. Pages are not dying. The “make a website” job is shifting toward the apps that cannot be a page. I want to be on that shift without pretending every form needs a research paper. The engine is the bet. The hope was the old job. I still write fetch. I just stopped calling it the future.

If you are learning frontend in 2026, learn the fetch path. You will use it all year. Then learn one sync path well enough to know what it costs. Do not collect engine names. Collect the questions: who writes, who is offline, who merges. Those questions will outlive the current logos. I have outlived three state libraries. I will not outlive the need for a truth model. Fetch-and-hope was a truth model that assumed a fat pipe and a single adult. The pipe got thin. The adults multiplied. The model has to change. That is the future I mean, and it is already in the apps people will not put down.

More articles for you