The three rendering musketeers: server-render, client-render and pre-render

This post is 4 years old. (Or older!) Code samples may not work, screenshots may be missing and links could be broken. Although some of the content may be relevant please take it with a pinch of salt.

In this article, we'll review the three main rendering options out there today: server-side rendering (SSR), client-side rendering (CSR) and pre-rendering. Before we get started, let's take a look at the history of the web and understand where these concepts come from.

A Brief History

In the early 2010s, the popularity of SPAs (Single Page Applications) began to rise - this was mainly due to the rise of front-end frameworks like AngularJS, advocating such design patterns for websites. The main premise of an SPA is to load an application shell - which is some HTML structure - and then based on user actions, change the content by leveraging HTML fragments. What we are talking about here are route changes that load content, without the need to reload the entire page (there's no full page reload).

Client-Side Rendering (CSR)

With client-side rendering, we do all the routing, templating, and data fetching in the browser. Most, if not all, the logic happens in the browser. Single Page Applications only fetch data from an API (such as a RESTful API) and provide that data to the client. It is up to the client to parse and display that data.

The browser loads and parses the HTML, the front-end framework would then bootstrap itself and take care of templating and routing amongst other things. The browser at this point becomes the main execution engine. There's a lot of JavaScript that needs to be executed, and unfortunately, this has a few negative aspects.

One is that SEO (Search Engine Optimisation) is nearly impossible. The reason for this is because the HTML loaded by the browser contains "placeholders" which the JavaScript framework of choice would populate at a later point in time. Some crawlers can't execute JavaScript, some can but with certain limitations.

Essentially the HTML that first gets parsed by the browser looks like this:

<h1>Welcome, !</h1>

The browser will render this because it's not aware of the meaning of the double curly braces, but once the frontend framework (in this case Angular) get loaded, it'll replace the value of user.name.

On top of the problems related to SEO, there's another problem which is related to web performance. Since the browser needs to execute a lot of code, placing a lot of strain on it, which in turn can potentially harm the user experience.

Developers quickly realised that there needs to be a better way of building apps - ones that perform well and can be search engine optimised more naturally.

Server-Side Rendering (SSR)

Enter server-side rendering. Popular JavaScript frameworks have options to render components and entire HTML pages on the server-side. This means that the code that we wrote in the language of the front-end framework can be executed on the server-side as well with minimal modifications. And the term 'isomorphic' / 'universal' JavaScript was born.

Read the article titled What Is Isomporhic JavaScript? to learn more about this concept.

The great thing about SSR is that we can leverage the server itself. It has better processing capabilities, potentially more stable internet connection which means that we can make API requests, parse the responses, add them to HTML, generate the final HTML and send that to the requesting client (i.e. the browser).

As you can see from the above, there's going to be a small delay when we request a page since the server would need to do all that work, but it's neglectable.

Using SSR, we can achieve SEO without any issues because the HTML which gets sent to the browser already has all the required data in it. The browser won't need to compile templates, and it doesn't have "placeholders", such things like <p>Hello, !</p> in the case of Angular, but instead, it has the full HTML: <p>Hello, Sam!</p>.

This is already a great approach - better SEO, less strain on the browser in terms of code execution.

The final rendering strategy utilises the best of both of these worlds. Let's review how.

Pre-rendering

Pre-rendering is a rather interesting concept because it uses some ideas found in server-side rendering, and some in 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 note that the HTML is not created at request time, but during the build, ahead of time. So in a way, when a user requests a page, the server is not doing any work.

There's a fundamental difference between letting the browser or the server do some work at request-time vs build-time. Request-time would be when someone clicks on a link/button and a new page / fragment needs to be loaded. Build-time means that all pages are created ahead of time so when a user is requesting a page, the HTML is already there, it just needs to be parsed by the browers.

The HTML that lands in the browser now needs to be (re)hydrated. Hydration (or re-hydration, or even client-side hydration) is a process where we take our static HTML and turn that into dynamic DOM which can accept client-side data changes and attach event listeners.

Think about it this way: the server has generated the static HTML already, and we don't want to do the same work on the client-side since it wouldn't make much sense. 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 leverage popular front-end frameworks and allow you to work in a familiar environment already to create static HTML pages.

Note that some static site generators use not only JavaScript but other languages as well such as Go and Ruby.

Some of the generators allow you to pick whether you'd like to work with "universal" mode or "server-side rendered" mode, and they can also create you a Single Page Application. A settings file of some sort usually controls these.

There's a lot of static site generators available, and each of them has various use-cases. Some are better at deploying thousands of pages, and some are better at creating components with the help of GraphQL (to query metadata for the pages such as titles) and then work with markdown files. Such a tool is great if you are building a blog or a personal website. Some other SSGs are suitable for building e-commerce pages, and some are were made for creating documentation sites.

Statig Site Generators are one of the enablers of the JAMstack. If you're interested in learning more about the JAMstack check out https://jamstack.training.

Conclusion

Hopefully, you now have a better understanding of how these three rendering mechanisms differ from each other. These days, the trend seems to be to use SSR, pre-rendering with hydration or a static site generator of some sort (this is also thanks for the rise of the JAMstack).

Each of these rendering options has valid use-cases, make sure you study each of them and that you gather enough information about the project/app to be developed to make a conscious decision before starting development.