
There are four ways to turn a React application into something a browser can show, and the choice is usually made once, by whoever picked the framework, and never revisited. Render at build time and every visitor gets a file. Render on the server, per request, and every visitor gets a fresh page and you get a server to run. Render in the browser and the device does the work. Or mix them, which is what most real products end up needing and few of them choose deliberately. The decision sets your hosting bill, whether a crawler ever sees your content, how fast the first screen appears, and who gets paged at three in the morning when rendering breaks. It is not a preference. It is a commercial decision with a technical implementation.
The vocabulary is worth fixing first, because most disagreements about this turn out to be definitional. Google's long-standing reference defines server-side rendering as "rendering an app on the server to send HTML, rather than JavaScript, to the client", client-side rendering as "rendering an app in a browser, using JavaScript to modify the DOM", prerendering as "running a client-side application at build time to capture its initial state as static HTML", and hydration as "running client-side scripts to add application state and interactivity to server-rendered HTML" (Addy Osmani and Jason Miller, Rendering on the Web, web.dev, published February 2019 and last updated January 2026). Four terms, four different bills.
Static rendering is the cheapest thing that is also fast, and it has one hard limit. It "happens at build time", offers "a fast FCP, and also a lower TBT and INP, as long as you limit the amount of client-side JavaScript on your pages", and achieves "a consistently fast TTFB, because the HTML for a page doesn't have to be dynamic". The constraint is the one that decides whether it fits your business: "one of the downsides to static rendering is that it must generate individual HTML files for every possible URL. This can be challenging or even infeasible when you need to predict those URLs ahead of time and for sites with a large number of unique pages." So marketing pages, documentation, pricing, a catalogue that changes daily — yes. A per-customer dashboard with a URL per account — no, and any attempt turns your build into the bottleneck. The practical tell is the build clock: if a content change takes forty minutes to appear, the strategy is fighting the business, not serving it.
Server rendering buys a fast first paint and costs you a server and a slower first byte. It "generates the full HTML for a page on the server in response to navigation", which "avoids additional round trips for data fetching and templating on the client", and it "generally produces a fast FCP" while letting you "avoid sending lots of JavaScript to the client". The trade is stated without euphemism: "generating pages on the server takes time, which can increase your page's TTFB." Translated into operations, you now run something that executes your application code on every request, which has to be sized, monitored, cached and paid for; a bad query in a component is now a production incident rather than a slow screen; and your caching strategy stops being a detail and becomes the architecture. That is a real bill, and it buys a real thing.
Client rendering is the cheapest to operate and the most expensive at the device. It means "rendering pages directly in the browser with JavaScript", where "all logic, data fetching, templating, and routing are handled on the client instead of on the server". The failure mode is structural rather than accidental: "the primary downside to client-side rendering is that the amount of JavaScript required tends to grow as an application grows, which can impact a page's INP." Nobody ever decides to ship a slow bundle. It accretes, one library and one polyfill at a time, and the cost lands on hardware you do not own and cannot upgrade.
The hybrid everybody actually ships is the one with the subtlest failure. Server rendering followed by hydration looks like the best of both and has a specific cost: "the primary downside of server-side rendering with rehydration is that it can have a significant negative impact on TBT and INP, even if it improves FCP", because such pages "can appear to be loaded and interactive, but can't actually respond to input until the client-side scripts for components are executed and event handlers have been attached". That gap is the most under-discussed source of complaints in production React, because it is invisible in a screenshot and invisible on a fast machine: the content is there, the button is there, the button does nothing, and the user presses it again. The same page adds the other half of the cost — to hydrate without refetching, most solutions serialise the data into the document, so the payload carries the content twice.
There is a measured threshold for how much client rendering is too much, and it is higher than purists claim and lower than convenience allows. The Web Almanac's 2024 analysis compared the initial HTML with the final HTML after JavaScript ran, and found the share of pages with good largest-contentful-paint stays at approximately 60% on mobile until client-side generated content reaches 70% of the page, after which it drops at a faster rate to about 40% (HTTP Archive, Web Almanac 2024: Performance). The chapter draws the conclusion plainly: a combination of server- and client-generated content does not significantly affect how fast the main element renders, while rendering a website entirely on the client does. That is an unusually actionable number. Mixing is fine. Emptying the HTML is not.
Then there is who else has to read the page. Google documents its own handling in three phases — crawling, rendering, indexing — and is explicit that the second is deferred: "Googlebot queues all pages with a 200 HTTP status code for rendering, unless a robots meta tag or header tells Google not to index the page. The page may stay on this queue for a few seconds, but it can take longer than that. Once Google's resources allow, a headless Chromium renders the page and executes the JavaScript" (Google Search Central, JavaScript SEO basics). Content that exists only after JavaScript runs does get indexed; it gets indexed later, on somebody else's schedule, and every other automated reader of your site is a separate bet you are making without documentation. If a page exists to be found, the case for putting its content in the HTML response is not a performance argument at all.
React's current answer collapses the either-or into a per-component decision. Server Components are "a new type of Component that renders ahead of time, before bundling, in an environment separate from your client app or SSR server", and — the sentence that matters for budgeting — they "can run once at build time on your CI server, or they can be run for each request using a web server" (React, Server Components, react.dev). The same component model covers the static case and the dynamic one, which means the static-versus-server argument stops being a framework-level religious war and becomes a series of small, reversible choices per route. That is genuine progress, and it moves the decision into code, where it has to be reviewed like code.
Streaming is the other half of that. Rendering to a stream "allows the user to start seeing the content even before all the data has loaded on the server", and it introduces one piece of vocabulary worth borrowing for planning conversations: "the part of your app outside of any Suspense boundaries is called the shell", and the shell "determines the earliest loading state that the user may see" (React, renderToPipeableStream, react.dev). Defining the shell is a product decision wearing an API's clothing. What must the user see immediately, and what may arrive a moment later? Answer that once, deliberately, and the slow database call stops holding the whole page hostage. Never answer it, and the slowest query on the route sets the speed of everything.
So here is the decision as a business would put it, in five questions. Is this page the same for everyone, or personalised? Does it need to be found by something that is not a person? What does a stale version cost — nothing, a support ticket, or a refund? Who operates the renderer, and what does it cost per month at your traffic, including the incident it will eventually cause? And how much of the screen is genuinely interactive rather than merely rendered once? Answer those five per route rather than per application, and the strategy usually writes itself: static for what is public and shared, server-rendered for what is dynamic but must be visible immediately, client-rendered behind a login where there is no crawler to satisfy and no first-paint race to win. web.dev's own summary points the same way — "we encourage developers to consider server-side rendering or static rendering over a full rehydration approach".
This is also why the question rarely arrives alone. A platform like the 150-branch student information system we built for Concordia is not one product: role-based portals for students, teachers and administrators, a revenue and royalty engine, and consolidated reporting for headquarters are four audiences with four different answers to the five questions above. Applying one rendering strategy to all of them is how a team ends up server-rendering an internal admin table nobody outside the company will ever crawl, while a public page that needed to be in the index waits in a queue behind it.
Two cautions before anybody replatforms. First, do not migrate a rendering strategy to solve a performance problem you have not measured — the fault is often somewhere else entirely, and why a React app is slow in production walks the five suspects in the order that costs least to test. Second, if the application is one you inherited rather than wrote, the rendering decision is downstream of questions about the codebase itself; what to check before you inherit a React codebase covers the ones that decide whether a migration is a project or a rewrite.
The reason this belongs in a commercial conversation rather than a technical one is that all four options work. None of them is wrong in general and all of them are wrong somewhere specific, and the cost of the mismatch is paid in infrastructure bills, in search visibility, and in the gap between a page that looks ready and a page that responds. A React development company should be able to tell you which routes belong in which bucket and why, in terms of your traffic and your content, before writing any code — and if you would rather have the whole system's readiness ranked before committing to a build, a fixed-fee AI readiness assessment produces that roadmap whether or not you build with us.
Related: React development company
Find this useful? Tell Google to show you more of it.
