SSR vs CSR vs SSG
Use static generation for content that can be prepared ahead of requests, server rendering when request-time data or context must shape the initial HTML, and client rendering where the browser should assemble the interface. An application can combine all three. Choose at the route or component boundary that has the requirement.
Separate two questions: when does useful HTML arrive, and when can the user interact correctly? Server-rendered or generated markup can still require substantial JavaScript. A fast first paragraph does not automatically mean a responsive application.
TL;DR
- SSR produces HTML using server-side work for a request; caching can change how often that work runs.
- SSG prepares HTML ahead of serving it, with an explicit rebuild or regeneration model for updates.
- CSR uses browser JavaScript to construct the interface and may still follow an initial HTML shell.
- Hydration attaches application behavior to matching server-produced markup; static HTML does not inherently need hydration.
- Compare initial content, interaction, freshness, cache correctness, and failures together.
- Make indexing and authorization explicit. Rendering location does not guarantee either search visibility or secure access.
Quick Example
This complete Node.js module illustrates the timing boundary without a framework. Save it as rendering.mjs, run node rendering.mjs, and visit the three local routes. Stop the process when finished. It binds only to loopback and serves fictional content.
Reload /static: its timestamp stays fixed for this process because the HTML was prepared once. /server prepares a new timestamp per request. /client has no heading in the initial main; its script creates one. This models timing only: it is not a production static build system, framework hydration example, cache benchmark, or deployment configuration. The deliberate no-store header keeps cache reuse out of this demonstration.
Core Concepts
Rendering has a place and a time
web.dev's rendering guide distinguishes server rendering, static generation, browser rendering, and hydration. The definitions describe where work happens; they do not dictate one permanent architecture for the whole application.
An article route might use generated HTML, a personalized account route might render on request, and a filter panel might update in the browser. Write down the initial response and update path for each instead of assigning the entire product a single three-letter identity.
Hydration is separate from producing HTML
A framework may produce HTML and then run client code to attach state and interactions. The initial client render must match the server's expectations. Nondeterministic output, different data, or browser-only behavior can create mismatches.
Static content can also be delivered without an application runtime. For example, React documents that renderToStaticMarkup produces non-interactive HTML that cannot be hydrated. Do not choose that API when the plan requires React hydration later.
Caching changes the request path
An SSR implementation can cache a response, so a later visitor may receive previously generated HTML. An SSG implementation can regenerate selected pages rather than rebuild every route. These are freshness and invalidation designs, not proof that the names are meaningless.
Identify the cache key, lifetime, variation, and invalidation trigger. Personalized responses require particular care: a cache that serves one user's representation to another is a correctness and confidentiality failure, even if its hit rate is excellent.
Compare the work and update path
These are decision dimensions. A framework can provide hybrid features that change the tradeoffs. Inspect its documented behavior and hosting support, including what happens when the preferred fast path fails.
Choose by freshness and user tasks
Start with the maximum acceptable age of the data. Documentation may tolerate a publishing delay; a private account balance needs a different update and access model. Do not infer the freshness requirement from whether the page contains a button.
Next, identify what must be in the initial HTML. A public product description and its navigation may need reliable initial delivery. A complex internal analysis panel may reasonably depend on authenticated browser requests. An interactive application can still benefit from server-produced structure without rendering every future state on the server.
Then decide how updates arrive. Generated pages need a publishing trigger and a way to observe failed or delayed regeneration. Request-rendered pages need authorized data fetching, a runtime capacity plan, and a cache policy. Browser-rendered pages need loading, error, retry, and navigation behavior. None of these approaches makes stale or missing data disappear automatically.
Finally, test one complete route with realistic data. Request it directly rather than only navigating from another page. Disable or delay scripts, make its data source slow, and introduce an error. The important result is whether the user can understand the state and complete the intended task, not whether the implementation fits a fashionable label.
Evaluate performance and indexing separately
Measure response timing, useful content visibility, JavaScript work, and interaction. Include a slower device and network, a cold cache, and a repeated visit. Server work can delay the first byte; browser work can delay content and interaction; generated output can become stale if the update system fails. Those are mechanisms to investigate, not universal rankings.
Google's JavaScript SEO documentation describes crawling, rendering, and indexing as distinct stages, and notes that not every crawler executes JavaScript. Initial HTML can improve robustness, but it does not guarantee indexing or ranking.
Verify route status codes, crawlable links, relevant metadata, and the actual rendered content. A client router that shows an error screen behind an HTTP 200 response can send a different signal from a server returning an appropriate error status. A generated route with an accidental indexing restriction has its own problem. Diagnose those outcomes directly.
Best Practices
Write a rendering contract per route
Record initial content, authorized data source, freshness target, update path, and fallback behavior. This makes hybrid choices reviewable and gives tests concrete outcomes to check.
Bound client work
Use client-side code for the interaction that needs it. Review dependencies and initialization cost instead of assuming server-rendered HTML removes browser work. Keep important content understandable during loading or partial failure.
Test invalidation and direct navigation
Update content and verify when users see it. Request nested routes directly, check errors, and confirm cache separation. Navigation success from the homepage is only one route into the application.
Common Mistakes
Equating SSR with automatic speed
Bad: Move rendering to a server and assume every performance metric improves.
Correct: Measure server latency, delivered content, client execution, and interaction under realistic conditions.
Equating SSG with permanent freshness
Bad: Publish generated output without a rebuild, regeneration, or invalidation plan.
Correct: Define the update trigger and acceptable delay, then test the failure and recovery path.
Treating CSR as an indexing verdict
Bad: Declare all client-rendered content invisible or automatically indexable.
Correct: Check crawler access, rendering dependencies, status codes, links, and actual indexing evidence for the route.
FAQ
Is Next.js SSR or SSG?
Its supported rendering modes depend on route design and configuration. Evaluate the actual route and host rather than treating the framework name as a complete rendering policy.
Can static pages be interactive?
Yes. Generated HTML can include client-side interactions. The decision is how much runtime work the feature needs and how it receives fresh data.
Does SSR remove the need for JavaScript?
Not necessarily. A server-rendered application may still send substantial JavaScript for hydration and interaction. Static non-interactive output is a separate choice.
Which mode should I choose first?
Choose the simplest supported approach that meets initial-content, freshness, access, and interaction requirements. Change that choice when a representative route exposes a concrete limitation.
Related Topics
- Web Performance — Measure the user's loading and interaction experience.
- Next.js vs React — Framework responsibilities and rendering decisions.
- React — Components, state, and client behavior.
- HTTP — Status and cache semantics beneath rendering.