How do I keep marketing pages static while personalizing only a few routes?
Most of our site is static marketing content and I want it to stay fully static. Only a handful of routes actually need personalized slots, things like the logged-in dashboard landing and a couple of campaign pages.
I learned that fetchContent turns a route dynamic. What I want is a clear architectural rule so that only the routes that personalize pay that cost and the marketing pages keep rendering statically. How do people structure the app for this?
2 answers
The rule is simpler than it looks: fetchContent reads the request, so any route that calls it opts into dynamic rendering. Everything else stays exactly as static as it was. So the architecture is just about where you put the call.
Keep fetchContent out of anything meant to stay static, and in particular out of shared layouts, since a call in a layout pulls every route under it into dynamic rendering. Put it only on the specific routes that actually personalize. Your marketing pages then remain fully static and your dashboard and campaign routes render dynamically, side by side in the same app.
The one thing I would push back on is trying to force personalization onto a route that should stay static. The docs deliberately recommend against that rather than papering over it. The rendering behaviour is documented in the Next.js rendering reference, and the call that triggers it is the server fetchContent function.
For anyone reading this later with the same question: the docs do not prescribe a specific ISR strategy for the personalized routes, so do not go hunting for a hidden switch that makes a personalized route static again. The split above is the intended shape.
the way we made this obvious in the codebase was route groups. one group for the static marketing site, a separate group for the personalized routes, and fetchContent lives only in the second one. keeps it impossible to accidentally drop a call into a shared layout and flip the whole marketing side dynamic. related background in why fetchContent forces dynamic rendering and why ISR serves everyone the same HTML