Web Rendering techniques - An overview

Exploring different ways of rendering webpages, some commonly used techniques in the world of web and their trade-offs

Where are pages rendered? What do terms like server-side rendering, and client-side rendering refer to? How should we render our apps? Where should we implement the logic to render our applications? Let's search for some answers ✨

Web applications follow several methods of rendering applications, some of which include -

  • Server-side rendering

  • Client-side rendering

  • Static Rendering

Each one of them comes with its own advantages and disadvantages, let's first look at them in detail.

Server-side rendering

Server-side rendering refers to generating the HTML content of your web application on the server before it is rendered and returning the entire generated page as the response to a request, as opposed to the way of templating from the client. In this case, we first return the HTML content from the server, and then "hydrate" this response or static markup in the browser, into your fully interactive web application. Since the generated page is returned from the server, it reduces the need of generating it on the client, which results in a lower FCP (First contentful paint time), also, since a lot of js need not be processed on the client, it may also reduce the time to interactive for such pages.

This helps us in achieving not only faster response times but also in supporting users with a poor internet connection or those working on legacy devices.

Since, the content would already be present before we "index" it, or need it, this approach is good for search engine optimization (SEO), search engines are able to crawl it just fine. This may not be possible otherwise, because some search engines are known to have problems with a router implementation, so your site may find it difficult to rank higher in search results.

Client-side rendering

Client-side rendering refers to performing all the operations related to rendering directly in the browser using javascript, which means things like templating, routing, fetching and all other logic is handled on the client directly and not on the server. This means that instead of getting the content from your HTML document itself, what you get is a bare-bone HTML document with a javascript file that will render the site using the browser. A typical response that the server would send while generating a React app would look like this -

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <link rel="shortcut icon" href="/favicon.ico">
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="/app.js"></script>
  </body>
</html>

This comes with a javascript file app.js, which is your actual site and will be "rendered" by the browser on the client side. This means, that the server is now responsible only for loading the boilerplate HTML and rest everything is handled by the client. This also means that your site won't be able to load until it gets all of this javascript in the browser, this might increase the initial loading time of the site and since the content won't be rendered until the page is loaded on the browser, search engine rankings and SEO may take a hit.

Static rendering

With static rendering, we need to generate a single HTML file for every page that the user can access ahead of time. These pages are then served from a cloud service like S3, or from a server running something like nginx. Static rendering is extremely fast - because nothing needs to be generated on the fly. Since all these responses are generated ahead of time, they can be served from a CDN and made even faster. However, this also means that we need to generate responses for every possible request beforehand. This may not satisfy many use cases, especially when there can be many unknowns.

Combining the best of both worlds

Server rendering comes with its own considerations, many solutions to implement server rendering are known to delay TTFB or double the data being sent, it needs to be implemented well. Some issues with server rendering are -

  • Everything would be sent across the wire twice, once as static HTML and once as javascript

  • Though we render on the server, we need to render again in the browser to hydrate it.

So how do we combat this overhead and not be at the mercy of javascript loading times to display our site?

Progressive Hydration (Lazy loading the javascript)

Difference between loading and lazy loading - getting assets on demand

Lazy loading is a known performance optimization technique. In progressive hydration, we won't load the javascript unless the user "interacts" with that part of the code. Probably loading when scrolled into view, or when clicked upon, it has its own considerations - Javascript frameworks need to hydrate top-down, also deferring an overhead is not making things any better, your site may have a better lighthouse score, but you are definitely making the user wait.

Server components

Let's combine partial hydration and re-rendering the static parts on the server - we get server components This lets us have the benefits of partial hydration with a reduced component code size. However, for implementing this, you need to maintain normal server rendering for the initial render, and a way to differentiate the existing HTML against the parts to re-render. This means we need a larger runtime environment in the browser. However, its benefits can be realized when dealing with large apps.

Another thing about modern javascript apps is the ability to be "isomorphic" - perform something called universal rendering, the fact that they can be run on both the client (browser) and the server. This helps us deal with web pages that are more dynamic. Libraries like Next.js, Gatsby, etc. help us implement SSR in React.

In this article, we explored some ways of implementing rendering logic, each one of them comes with its own set of advantages and trade-offs. The best solution might be a mixture of some of these techniques. But probably now we have more insights on some challenging problems with modern javascript 😄