The front-end development landscape is currently undergoing a transformation. Server-side rendering (SSR), particularly with React’s built-in server-side hydration capabilities, is gaining significant traction. However, it’s not the sole method for achieving a fast user experience with excellent time-to-first-byte (TTFB) scores. Pre-rendering presents another compelling approach. Let’s explore the distinctions between these solutions and a fully client-rendered application.
Client-Side Rendering: An Overview
Since the advent of frameworks like Angular, Ember.js, and Backbone, client-side rendering has become ubiquitous in front-end development. This approach works well, even for SEO, thanks to search engines’ ability to process JavaScript.
In a client-side rendering setup, the server responds to requests with a barebones HTML file, often displaying a loading screen. The actual content appears only after the browser downloads, parses, and executes all the necessary JavaScript.
While this method performs adequately under favorable network conditions, there’s room for optimization without excessive complexity. Let’s delve into alternative approaches.
Server-Side Rendering (SSR): A Renewed Focus
SSR, a practice prevalent in the early days of web development, experienced a decline in popularity compared to client-side rendering. Here, we’ll examine its resurgence and explore its mechanics.
Traditional SSR involved generating fully populated HTML pages on the server, often using languages like PHP. This ensured quick initial page loads.
However, each navigation request necessitated a repetition of this process – fetching the server-side code, compiling it, and sending the complete HTML, CSS, and JavaScript – leading to noticeable delays.
What if we could combine the initial page load speed of SSR with the dynamic routing capabilities of client-side frameworks using AJAX to fetch only the required data?
This is precisely why SSR is regaining prominence, driven by frameworks like React that offer user-friendly solutions like the RenderToString method.
Applications built using this hybrid approach are often termed universal or isomorphic apps, although the precise definitions remain somewhat fluid.
The key advantage lies in the ability to use a single codebase for both server-side and client-side rendering, resulting in a fast and data-rich user experience. The trade-off is the requirement for a server.
SSR excels at leveraging the server’s robust internet connection to prefetch and assemble data before sending it to the user, whose connection (especially on lie-fi) might be less reliable.
Moreover, SSR addresses a limitation of client-side rendering related to social sharing and OpenGraph metadata. With only a single index.html file, social media platforms receive generic metadata, typically from the homepage, regardless of the actual page being shared. SSR resolves this by providing contextually relevant metadata for each route.
Pre-rendering: A Lightweight Alternative
The need for a dedicated server in SSR might not be ideal for all projects, especially smaller ones. This is where pre-rendering shines.
Frameworks like Preact, with its dedicated CLI, enable pre-rendering selected routes into static HTML files that can be served from any static hosting solution. This delivers a fast initial load without relying on Node.js, thanks to technologies like Preact/React hydration.
However, unlike SSR, pre-rendering lacks access to user-specific data. The initial response is a static, generic representation of the page. To mitigate potential user frustration while waiting for personalized data, consider incorporating a well-designed skeleton loader:

Another consideration is the need for a routing mechanism to direct requests to the appropriate pre-rendered files.
Routing in Pre-rendering
Single-page applications typically route all requests to the root file, leaving internal routing to the framework. Pre-rendering, however, requires a proxy or similar mechanism to map specific routes to their corresponding HTML files.
For example, a site with four routes (/, /about, /jobs, and blog), each with a unique layout, necessitates four distinct HTML files to provide an optimal loading experience. If all requests were routed to the root index.html, users would experience jarring layout shifts as the browser loads and replaces the incorrect skeleton.
The solution involves configuring a proxy to map each route to its designated file:
https://my-website.com→index.htmlhttps://my-website.com/about→/about/index.htmlhttps://my-website.com/jobs→/jobs/index.htmlhttps://my-website.com/blog→/blog/index.html
This highlights why pre-rendering is well-suited for smaller applications – managing numerous routes in this manner can become cumbersome.
It’s worth noting that this explicit routing isn’t strictly mandatory for simple cases. Routes like https://my-website.com/about/ work automatically because the server defaults to serving index.html from a directory. However, the proxy becomes crucial for parameterized URLs. For instance, https://my-website.com/profile/guillaume would need to be directed to /profile/index.html, as a file named profile/guillaume/index.html wouldn’t exist, leading to a 404 error.

To summarize, the three rendering strategies discussed utilize a combination of loading screens, skeleton loaders, and fully rendered pages, depending on the specific approach.

Only in a particular scenario does this approach fall short:
| Method | Landing (e.g. /) | Static (e.g. /about) | Fixed Dynamic (e.g. /news) | Parameterized Dynamic (e.g. /users/:user-id) |
|---|---|---|---|---|
| Client-rendered | Loading → Full | Loading → Full | Loading → Skeleton → Full | Loading → Skeleton → Full |
| Pre-rendered | Full | Full | Skeleton → Full | HTTP 404 (page not found) |
| Pre-rendered With Proxy | Full | Full | Skeleton → Full | Skeleton → Full |
| SSR | Full | Full | Full | Full |
Beyond Client-Side Rendering
Given the advancements in rendering techniques, relying solely on client-side rendering is no longer the optimal choice. Pre-rendering, in particular, offers a relatively simple yet effective way to enhance user experience. It surpasses client-only rendering and is often easier to implement than a full-fledged SSR setup.
SSR/universal applications are powerful for larger applications with extensive content, ensuring focused and relevant information is presented to search engine crawlers and social media bots. This, in turn, positively impacts search engine rankings, where performance is now a significant factor.
Stay tuned for a practical tutorial where we will transform a single-page application into pre-rendered and SSR versions, comparing their performance along the way.