Map the request before choosing the stack
A rendering decision is a delivery decision. Before comparing frameworks, write down what a visitor needs to see, how often that information changes, and whether it is different for each person. A documentation page updated weekly has a different job from an account dashboard. They can share a design system without sharing a rendering strategy.
Start with a small inventory: public editorial content, frequently changing public information, and private personalized views. For each group, identify the source of truth and the longest acceptable delay before a change appears. This turns “we need something dynamic” into a concrete requirement that can be tested.
Understand the three useful starting points
Static generation creates HTML before a request arrives. That makes published pages straightforward to cache and serve, but updates depend on a successful build or regeneration process. Consider what happens when that process fails: older content may remain available, which is helpful for an article but inappropriate for some time-sensitive information.
Server rendering creates HTML in response to a request. It can incorporate current or personalized data, but now the server and its upstream dependencies participate in the visitor’s wait. Cache boundaries, timeouts, and fallback content matter as much as the rendering code.
Client rendering asks the browser to assemble some or all of the interface. It can support rich interactions, but a useful page should not accidentally depend on a large script download simply to reveal a heading and paragraph. Rendering HTML on the server also does not automatically mean sending less JavaScript; hydration can still be expensive.
Make a decision with failure in view
For a small public publication, static HTML is often a reasonable baseline. Add server work when freshness or identity genuinely requires it. Add client behavior where interaction needs it. These are composable choices, not three mutually exclusive brands of website.
Sketch the failure cases before committing. What appears if JavaScript is unavailable? What if an API takes several seconds? Could a shared cache expose one person’s private response to another? Personalized responses need carefully designed cache controls, not a blanket “cache everything” rule.
Also budget for the publishing workflow. Previewing changes, invalidating caches, rebuilding a large archive, and rolling back a broken release all have operational costs. A simple page architecture with a confusing release process is not simple for the people maintaining it.
Run a small, representative trial
Build one realistic page with its real content shape, one interactive element, and a slow or failing data source. Inspect the HTML response, disable scripts, and test navigation on a constrained connection. Record the scripts shipped, the cache behavior, and what remains usable during failure. Avoid treating a demo’s timing as a universal benchmark.
Write a short decision note: the requirements, the chosen approach, the alternatives rejected, and the condition that would trigger a review. A content freshness requirement changing from days to seconds is a good reason to revisit a decision. A new framework announcement is not, by itself, one.
MDN: HTTP caching
Use the authoritative documentation for implementation details and evolving guidance.
Continue reading
A dependency is a maintenance decision. →