How to choose web page rendering to support SEO and performance
A good software developer who builds web applications should understand the differences between various page rendering techniques and how those differences impact requirements fulfilment. So, if you are unsure about the differences between server, client, static, hybrid and progressive rendering, and you struggle to choose the right frontend architecture for your project, then this article is definitely for you.
What is web rendering?
Unfortunately, no common standard used by the software developers community describes precisely what different page rendering techniques mean. Therefore often, various sources provide contradictory definitions introducing miscommunication between developers that leads to bad technical decisions that can affect achieving business goals. This post will define the differences between page rendering techniques and highlight how they support system requirements like performance, SEO, and user content customisation.
Let's start by defining the page rendering as in frontend development; we have two different processes with the same name that follow each other. The first rendering is building website content by your application that combines your page components definition, like navigation, footer or article, with application data like logged user details or article content. As a result, we get the HTML document or directly make the operations on Document Object Model that represent an HTML document in the browser memory. Let’s call this process the Application Page Rendering.
A browser engine handles the second rendering process, which turns an HTML document into an interactive visual representation on the screen. Browser parse the HTML into Document Object Model and loads external resources like CSS, images, and JavaScript (that can block this process, by the way). In the next steps, the browser parses and calculates the styles for each element, builds a layout tree, sets the painting order and turns this information into pixels on the screen (rasterising). Let’s call this process the Browser Page Rendering.
We can’t change the Browser Page Rendering process. Still, we can optimise our HTML Document to ensure that the browser engine can quickly render the page. This topic is so important and large that there will be a dedicated post just for Browser Page Rendering optimisations. Today we will focus on different Application Page Rendering techniques as that is the architecture decision we need to take in the early stage of the project.
Server-Side Rendering (SSR)
In Server Side Rendering, we have a working web application on the server-side that handles the page request from the browser. The application takes our templates files or components definition based on request details, combines them with data pulled from databases or APIs and returns the full website content in the first HTTP response. Pure Server-Side Rendering is the traditional way of building websites, usually available for most back-end applications written in Java, PHP, Ruby or Python. Over time, pages generated on the server-side got the JavaScript scripts that added some interactivity to the websites like drop-down menus or simple animations. Still, those scripts were just the addons that do not impact how the application renders the page.
Client-Side Rendering (CSR)
In Client-Side Rendering, the browser gets an HTML document with an empty body in the first response and links to JavaScript application files. The browser loads and executes our JavaScript application in the next step, optionally fetching additional data from the server if needed and combining it with components definition. As a result, we add page content directly into Document Object Model. JavaScript is not an add-on to add interactivity the to page but the core application that generates the final page content in the browser. By default, that is how most modern JavaScript tools like React, Vue, or Angular work.
Pre-rendering or static rendering (SSG)
Static rendering, sometimes called pre-rendering, is like Server Side Rendering. In the first response, the browser gets the full website content. Instead of rendering the page on demand like for SSR, the application render the pages and save them in static HTML files in advance, often as part of the build process or in response to some events, like the content update. The server responsibility brings down to serve previously rendered static files. The tools that generate the static files during the build process or in response to defined events are called Static Side Generators (SSG). We do not assume that static rendering runs JavaScript on the client-side. Therefore we think more about the tools like Jekyll, Hugo, or Eleventy rather than NextJs, NuxtJS or Gatsby, as those tools follow isomorphic static rendering.
Isomorphic rendering
Isomorphic rendering mixes server-side (or static rendering) with client-side rendering by sharing the same JavaScript application code between the server and browser. The browser gets the full HTML website content in the first HTTP response and links to JavaScript application files. Once the browser loads the JS application, we render our application on the browser-side again, hydrating to the already rendered page. Hydration is attaching event handlers to HTML elements generated previously on the server-side. Please note that despite having initial content loaded from the first HTML request, modern tools render again to calculate where to attach event handlers and make your application fully interactive, which has its performance implications.
Dynamic Rendering
Dynamic rendering behave differently based on the detected user agent. If the server detects that the page wants to be loaded by a search engine, e.g. Googlebot, we serve a static page version; otherwise, the Client-Side application.
Progressive enhancement rendering
Progressive enhancement rendering focuses on serving the most important content in the first place and gradually adding the rest of the content and functionality in the following steps.
Hybrid rendering
I found three common definitions of hybrid rendering:
-
The same meaning as Isomporhic rendering
-
The same meaning as Progressive enhancement rendering
-
Using rendering techniques per page individually:
Because we already defined Isomorphic and progressive enhancement rendering, let’s define hybrid rendering as an option to set rendering techniques for pages individually.
How web rendering impact SEO
If we build public pages like e-commerce product pages or landing pages, we must ensure that those pages are well indexed by search engines like Google Search or Bing. Modern web crawlers can index content generated on pages by JavaScript on the browser side. Still, many SEO specialists suggest that to make indexing of our pages more reliable, we should ensure that content that should be indexed is in the first HTTP response from the server. Moreover, Dynamic Rendering, which sounds a little bit like a workaround for web crawlers, is recommended by Google Search as a way to improve SEO. Therefore, if our priority is to support SEO, we should pay attention when we plan to use modern JavaScript tools like React, Vue or Angular. Those tools require more setup to work with other rendering techniques than client-side rendering. For example, NextJs, NuxtJs and Angular Universal are the tools that enable server-side or static rendering and make React, Vue and Angular work as Isomorphic applications. We must remember that, especially in recent years, performance also impacts SEO.
How web rendering impact web performance
Of course, performance in web development is a huge topic, so we will only highlight some common differences between rendering techniques. First, let’s define our measurements. We have some good measurement standards that Google promotes, and we will focus on three of them:
-
Time To First Byte (TTFB) - how fast we start receiving the first piece of data when making a new HTTP request,
-
First Contentful Paint (FCP) - how fast we have displayed the first content on the screen,
-
Time To Interactive (TTI) - how fast the page is fully rendered and ready to respond to user input.
Suppose we run our application on the server-side that needs to render the page on-demon to provide the HTML document in response (server-side or isomorphic rendering). In that case, it affects Time To First Byte as it is much faster to serve previously rendered static files (static rendering) or empty pages with links to the javascript application (client-side rendering). In addition, we need larger servers to run our application on the server-side compared to the static servers, whose responsibility is to serve previously generated files from the hard drives.
If we have the HTML document in the first response from the server (server-side, static and isomorphic rendering), then the browser can start Browser Page Rendering right away, so we improve First Contentful Paint.
Suppose we have the application that renders in the browser (client-side and isomorphic rendering). In that scenario, we may affect Time To Interactive. The browser engine has to render the application in the browser, which takes time, especially on older mobile devices where CPU power is much more limited. It makes Isomorphic applications challenging to get a 100% performance rate on Google Page Speed Insight.
How web rendering impact content customization options
As software developers, we often need to display dynamic or customised content for the user, for example, user orders on an e-commerce page or flights on an airline booking page. It’s not a problem for server-side rendering as we run an application rendering for each HTTP request with access to user session and request parameters so we can customise the response depending on actual needs. For client-side rendering, displaying customised or dynamic content is not a big deal. Client-side rendered applications can pull data from the server before rendering using AJAX, assuming that you can ignore SEO and performance implications.
Things make a little more complicated when we have static rendering. Displaying customised content for users can be handled by adding progressive rendering at the top of static rendering. In that case, the browser displays common content for all users generated in advance as the first step. In the next step, we run the script in the browser that pulls and displays user-customized content. Dynamic data also can be challenging. For example, e-commerce applications can have thousands of product pages. In that case, generating all pages in advance can take hours.