Re-enabling SSR on a Next.js app trapped behind redux-persist
There is a kind of bug that is hard to see because nothing breaks. The page renders. The data shows up. Lighthouse gives you an okay-ish score. And yet somewhere along the way, your Next.js app has quietly become a single-page application that bills itself as server-rendered.
This is the story of one of those.
The setup
We had a Next.js app at eBuddy that needed offline-friendly state on the
client. Someone (me, eventually) reached for redux-persist. It works,
it’s familiar, and it gets the job done. The catch is that
redux-persist writes to localStorage, and localStorage does not
exist on the server. The standard workaround is to wrap the app in a
PersistGate that waits for hydration before rendering anything.
Functionally fine. Architecturally, it means the entire tree under that
gate renders empty on the server.
We had effectively disabled SSR. None of the marketing pages, none of the listings, none of the SEO-relevant content was reaching crawlers.
The fix
The fix is not to remove redux-persist; it earns its keep on the
client. The fix is to keep state hydration off the critical path so the
server can still render the parts that matter.
A few things stacked together: splitting the persisted slice from the non-persisted state, pushing the gate down to a sub-tree, and accepting that some state arrives a beat late on the client. The render-blank moment on first paint is gone.
The SEO score lifted by 50%. The JS bundle shrank. Nothing about the user-facing experience felt different, which is the whole point.
The lesson
The lesson, the one I keep relearning: the libraries you reach for first will shape the architecture you end up with. There is no such thing as a neutral dependency. Pick the ones that fit the rendering model you actually want, not the ones that look familiar.