The three rendering musketeers: server-render, client-render and pre-render
Older Article
This article was published 7 years ago. Some information may be outdated or no longer applicable.
We’ll review the three main rendering options available today: server-side rendering (SSR), client-side rendering (CSR) and pre-rendering. But first, a quick look at where these concepts come from.
A Brief History
In the early 2010s, SPAs (Single Page Applications) started gaining ground, mainly thanks to front-end frameworks like AngularJS that pushed this design pattern. The premise of an SPA is to load an application shell (some HTML structure) and then swap content based on user actions using HTML fragments. Route changes load content without reloading the entire page. No full page reload.
Client-Side Rendering (CSR)
With client-side rendering, all the routing, templating and data fetching happens in the browser. Most (if not all) the logic lives in the browser. Single Page Applications only fetch data from an API (such as a RESTful API) and hand that data to the client. The client parses and displays it.
The browser loads and parses the HTML, then the front-end framework bootstraps itself and takes care of templating, routing and other tasks. The browser becomes the main execution engine. There’s a lot of JavaScript to execute, and unfortunately, this carries a few negative consequences.
One: SEO (Search Engine Optimisation) is nearly impossible. The HTML loaded by the browser contains “placeholders” that the JavaScript framework will populate later. Some crawlers can’t execute JavaScript. Some can, but with certain limitations.
The HTML that first gets parsed by the browser looks like this:
<h1>Welcome, {{ user.name }}!</h1>
The browser renders that as-is because it doesn’t know what the double curly braces mean. Once the frontend framework (Angular, in this case) loads, it replaces the value of user.name.
On top of the SEO problems, there’s a performance problem. The browser needs to execute a lot of code, which places strain on it and can harm the user experience.
Developers quickly realised there had to be a better way to build apps. Ones that perform well and can be search engine optimised naturally.
Server-Side Rendering (SSR)
Enter server-side rendering. Popular JavaScript frameworks have options to render components and entire HTML pages on the server. The code written in the framework’s language can execute on the server with minimal modifications. And the term ‘isomorphic’ / ‘universal’ JavaScript was born.
Read the article titled What Is Isomorphic JavaScript? to learn more about this concept.
The great thing about SSR: we can lean on the server itself. It has better processing capabilities and a potentially more stable internet connection, so we can make API requests, parse the responses, inject them into HTML, generate the final HTML and send that to the requesting client (the browser).
There’ll be a small delay when requesting a page since the server needs to do all that work, but it’s negligible.
With SSR, we can achieve SEO without issues because the HTML sent to the browser already has all the required data. The browser doesn’t need to compile templates. There are no “placeholders” like <p>Hello, {{ user.name }}!</p> (in the Angular case). Instead, you get the full HTML: <p>Hello, Sam!</p>.
Already a great approach: better SEO, less strain on the browser for code execution.
The final rendering strategy pulls the best from both worlds. Let’s look at how.
Pre-rendering
Pre-rendering is an interesting concept because it borrows ideas from both server-side and client-side rendering. With pre-rendering, some HTML gets generated at build time on the server. That HTML is then sent to the requesting client, but the HTML isn’t created at request time. It’s created during the build, ahead of time. So when a user requests a page, the server isn’t doing any work.
There’s a fundamental difference between letting the browser or the server do work at request time vs build time. Request time is when someone clicks a link or button and a new page or fragment needs to load. Build time means all pages are created ahead of time, so when a user requests a page, the HTML is already there. It just needs to be parsed by the browser.
The HTML that lands in the browser now needs to be (re)hydrated. Hydration (or re-hydration, or client-side hydration) is a process where we take static HTML and turn it into dynamic DOM that can accept client-side data changes and attach event listeners.
Think of it this way: the server has already generated the static HTML, and we don’t want to redo that work on the client side. So we take the available static HTML and hydrate it to make it interactive.
The term “universal app” also refers to this functionality. A universal application sends HTML with data to the browser. The browser loads some JavaScript and rehydrates the page to get a dynamic, client-side application.
+1: Static Site Generators (SSG)
Static site generators build on top of popular front-end frameworks and let you work in a familiar environment to create static HTML pages.
Some static site generators use not just JavaScript but other languages as well, such as Go and Ruby.
Some generators let you pick between “universal” mode and “server-side rendered” mode, and they can also produce a Single Page Application. A settings file usually controls this.
There are a lot of static site generators available, each with various use cases. Some are better at deploying thousands of pages. Some excel at creating components with GraphQL (to query metadata for pages like titles) and work with markdown files. That’s great if you’re building a blog or a personal website. Others are suited for e-commerce pages, and some were made for documentation sites.
Static Site Generators are one of the enablers of the JAMstack.
Conclusion
You should now have a better understanding of how these three rendering mechanisms differ from each other. The trend these days is to use SSR, pre-rendering with hydration, or a static site generator (also thanks to the rise of the JAMstack).
Each rendering option has valid use cases. Study each of them. Gather enough information about the project or app you’re building to make a conscious decision before starting development.