Shreyash Tripathi reported that his portfolio’s mobile Lighthouse 12 Performance score rose from 30 to 80 after he prerendered its known routes and made several other performance changes. His build generated route-specific HTML, then the browser hydrated that content. The result is a useful Vite and React case study—not a guarantee that prerendering alone will produce the same score on another site.
What changed in the portfolio
Before the change, the portfolio relied on client-side rendering for most of its visible page content. The browser had to download and run JavaScript before it could render much of the interface. Tripathi changed the build so it produced HTML for each known route in advance. On a visit, the browser could receive meaningful page markup immediately, then run the client app to make it interactive.
As an Amazon Associate I earn from qualifying purchases.
The implementation follows a familiar split: a shared React app, a server entry that renders a route, and a client entry that hydrates the returned HTML. Vite’s SSR guide describes this general structure, while noting that its lower-level SSR API is primarily aimed at library and framework authors; it is not the only route for application developers. Vite’s SSR guide
How the build generated route HTML
Render each known route during the build
The project’s build command first ran the normal Vite build, then an SSR build for src/entry-server.tsx, and finally a Node prerender script. The server entry used React’s renderToString together with React Router’s StaticRouter to render the app for a specific URL.
#1 Best Overall
The script iterated through the portfolio’s known routes, rendered each route, and inserted its markup into the HTML template in place of the root placeholder. The output was therefore a set of HTML pages prepared ahead of deployment rather than one mostly empty app shell.
Hydrate the existing markup in the browser
The client entry checked whether the root element already contained children. If it did, it called hydrateRoot so React could attach behavior to the generated HTML. If no markup was present, it fell back to createRoot. Hydration depends on the server-rendered and client-rendered trees matching; inconsistent routes or providers can cause problems.
Rank #2
React’s documentation distinguishes static generation from rendering on each request and notes that adding a build-time static-generation setup can increase setup and maintenance work compared with a simple single-page app. React’s app-from-scratch guidance
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Prerendering was one part of a broader optimization
The before-and-after comparison covered multiple changes, so it cannot identify how much improvement came from prerendering alone. Alongside generating route HTML, Tripathi reports that he:
- Deferred interactive effects so they did not block the initial render.
- Delayed a chart until it was near the viewport.
- Compressed the profile image.
- Changed animation techniques and reduced costly blur effects.
- Adjusted font loading.
- Removed an autoplay boot screen. Tripathi’s description in the case study: “It was fun. It was also my LCP.”
That mix matters: putting content in the initial HTML can help it appear sooner, but heavy scripts, images, fonts, and effects can still affect loading and responsiveness.
What the author measured
Tripathi reported the following before-and-after results from Lighthouse 12 in mobile emulation. These are the author’s lab measurements, not field data, an independent replication, or a controlled test of prerendering in isolation. Read the case study
Rank #4
| Measure | Before | After | What it describes |
|---|---|---|---|
| Performance score | 30 | 80 | Lighthouse’s composite mobile lab score |
| Largest Contentful Paint (LCP) | 8.1 s | 3.4 s | Time until the largest visible content element is rendered |
| Total Blocking Time (TBT) | 3,390 ms | 240 ms | Main-thread blocking time during the Lighthouse lab test |
| Cumulative Layout Shift (CLS) | 0.014 | 0 | Measured visual layout instability |
| Words visible without JavaScript | 134 | 1,007 | Visible text in the page before client-side JavaScript runs |
The figures demonstrate the outcome on this portfolio under the author’s test conditions. They do not establish what another app will score, or how much any single change contributed.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Choose a rendering approach based on the app
Prerendering, request-time server-side rendering, and client-side rendering solve different delivery problems. The useful choice depends on whether routes and their content are known in advance, whether they vary per visitor, and how much runtime and maintenance complexity the project can accept. web.dev’s rendering overview
Best Value
| Approach | When HTML is generated | Good fit | Trade-offs |
|---|---|---|---|
| Build-time prerendering / static generation | During the build, ahead of a request | A finite set of routes whose content is known or can be fetched at build time, such as a portfolio | Can serve ready-to-render HTML from static infrastructure, but every required route must be generated and updated when its content changes. Very large or unpredictable URL sets can be difficult to enumerate. |
| Request-time SSR | On the server for each request, or as needed by the server’s caching strategy | Pages that depend on request-specific data or personalization | Can produce a response for dynamic requests, but server rendering adds work at request time and requires a runtime server setup. |
| Client-side rendering | Primarily in the browser after JavaScript loads | Apps where the initial page need not expose substantial content before the client app runs | Simple deployments can be attractive, but users may wait for scripts before meaningful content appears. Moving to prerendering does not remove the need for client JavaScript if the page requires interactive behavior. |
Static HTML can be deployed to a CDN, and static rendering can support fast first content paint and time to first byte. That does not mean a prerendered page is automatically interactive: hydration and other client scripts still have to execute. As web.dev explains, hydration can delay interaction while JavaScript runs, and prerendering that improves an SPA’s initial paint is not the same as delivering a page that needs little client-side JavaScript.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Implementation details that can trip up hydration
Keep the rendered app tree consistent
The route and shared providers used by the server render need to correspond to the client tree that hydrates it. If they produce different markup, React may not be able to hydrate the HTML cleanly. In this case study, the author found that the shared providers and route tree had to be represented consistently.
Be careful with Suspense and lazy components
Tripathi reports that a lazy component inside Suspense caused the renderToString path to emit a fallback to client rendering. If a route’s server output depends on a lazy boundary, test the generated HTML and hydration behavior for that route rather than assuming the server render contains the final component.
React documents separate static rendering APIs, which are relevant when generating static HTML rather than rendering a response for a live request. React’s static React DOM APIs
How to evaluate the result on your own app
- List the URLs you can know at build time. If page content depends on each visitor or changes per request, decide how that data will be delivered before choosing build-time output.
- Generate and inspect HTML for representative routes. Confirm that the initial document contains the content you expect before JavaScript runs, including route-specific content rather than just a shared shell.
- Test hydration and interactive behavior. Check routes with providers, lazy components, and Suspense boundaries, and look for mismatches or client-rendered fallbacks.
- Measure with repeatable conditions. Compare the same routes and mobile test settings before and after changes. Treat a Lighthouse result as a lab signal, not a promise about every visitor’s experience.
- Profile React work when needed. React’s
<Profiler>can measure rendering of a tree, but profiling adds overhead and is disabled in production by default. It is a diagnostic tool, not proof that prerendering will improve a particular site. React Profiler reference
When this approach is a sensible fit
A portfolio with a manageable set of mostly stable URLs is a natural candidate for build-time HTML generation: the pages can be prepared ahead of time, and the browser can receive readable content before the React app hydrates. An app with request-specific pages may instead need request-time rendering or another way to supply that dynamic content. Whichever route you take, treat rendering strategy as one part of performance work—not a substitute for addressing large assets, main-thread tasks, layout shifts, and client-side code.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




