Why React Router v7 Doesn't Have A Good PWA Solution

(This post was inspired by a conversation on the Remix Discord server, hence it's more casual and conversational.)

I spoke about local-first apps and React Router PWAs at Epic Web Conf in 2024. My app was kinda effective, but not really. I ended up wishing I'd built it as a native app from the beginning, and ended up paying for another app to serve the purpose . (The brilliance of that app is that it synchronizes meal plans using calendar entries and grocery lists using reminders - offloading the tricky sync stuff to the OS!)

Where PWAs gets tricky is when you add offline support. The biggest challenge is synchronizing server-rendered pages with client-cached data. You want to have SEO for some pages, but the data on the client might be different from the data rendered by the server. That's no good.

It's much easier to do offline for a fully client-rendered app, since the app has everything that it needs to navigate between pages without needing to make requests to the server (assuming it's cached all of the data that it needs, which is a big assumption and could be a tricky part.)

What does that look like for a server-rendered app? You download every possible page the user could visit? Not unreasonable, but still a lot of work. And then storing images for offline viewing is a whole extra can of worms.

React Router mixes Server-rendered pages with Client-rendered navigations, so it gets extra weird, which is why there isn't a good solution for it yet. I kind of hacked around it for my app, but the user experience was less-than-ideal.

All that to say, unless you need the benefits of SSR, I would actually do a client-rendered app if I were making an offline PWA.

That's not to say you can't add PWA features to a server-rendered app. Push Notifications and more intelligent caching are pretty easy to add with a service worker. It's just the offline part that's really tricky. (And I haven't even gotten into the multiple offline writers problem.)

I'd also look into local-first tools and sync engines with persistent client stores . Those can be useful to keep local caches of data in sync when the app is online.

But going deep into that is a post for another day.