10 ways to render a webpage

There used to be a new JavaScript library every day. That was fine, I didn’t care, I continued to implement my web apps using Angular or React or whatever it was.

But “everyone” agreed on which was the best way to render a web app. It was not the ideal way but at least everyone was happy to do the same mistake. Now there is a new problem. Even if you continue to use just React or Angular or whatever, you still have to decide what is the best way to render your web pages.

Here I will document the options available at the beginning of the year 2023. The list may be much longer this time next year.

What do I mean by a rendering way/mode/pattern/style? Is the way code and data are transformed in HTML. You can think of rendering as a glorified pure function that takes some data as input and returns HTML as output.

Concrete examples:

HTML. The backbone of the Internet for decades.

It is still that because it has many advantages, most important is available on almost any device with a screen - it’s the true Write once, run anywhere Holy Grail of software. Another major advantage of HTML is accessibility.

Also, HTML doesn’t break. It’s very forgiving. Is compatible with the first page of the internet and will be compatible with the last.

There are many ways to render the HTML for a website. So many that I’ve lost track.

I’m lost in the sea of cryptic rendering modes: SPA, MPA, SSR, SSG, ISR, OMG, and WAT. It’s time to clear things up.

When I think of rendering, the key issues that are at stake:

The list is ordered by the time each technology gains popularity. It might not be an exhaustive list, somewhere someone might already come up with a different way to render it. As with JavaScript libraries, this is becoming a never ending story.

1. Static website

The original way of rendering web pages. The data is text and the text is in HTML. There is no code.

Static website

These days, besides the text we can also add some CSS to make the page look better and maybe some JavaScript to add some small interaction flourish like light & dark mode theme switch or big tracking and analytics libraries.

You put the HTML files on a CDN and the files will be sent to browsers exactly as they are.

The classic example is the first page of the internet. Many blogs are static websites. Many marketing websites.



1.1 ✨ Using a static site generator

If you’re using (relatively) static data - like blogs, marketing sites, ranking sites, magazines, etc. - the most pressing issue of maintenance can be solved using a static site generator - a tool that takes some code, templates, and data source to generate the HTML pages of the website.

The advantage of using a static site generator is not only maintenance (by making changes in a single place), but you can keep your content in different formats (like markdown) or CMSs.

Popular options are Hugo, Jekyl, and 11ty.



2. Multi-page application (MPA)

Sometimes your website needs pages rendered with data that changes. When a page is requested, the server loads data and creates the HTML on the fly based on that data.

There is code that runs on request.

Multi-page website

A classic example is Amazon.com. Another notable example is any WordPress site that powers 43% off all internet.

For this kind of website, you use a server framework like Express, Rails, Laravel, ASP.Net, and an actual server computer. The server framework you choose depends on the programming language you use to write the code. Today (almost) no one has an actual server, they use Cloud providers.



3. Single Page Application (SPA)

A more general name for this technique is Client-Side Rendering(CSR). I’m using the SPA term because it’s more popular.

When AJAX started to gain popularity, many websites started to transform into web apps. AJAX provides a way to make a server call to get some data without the need for a full page refresh.

These web apps have a single page. A blank page initially. Then the JavaScript loads and it creates the HTML. And makes the HTML interactive (by adding listeners.)

When you click a button, something happens, maybe a call is made to the server (API) and the data returned by the call is used to update a part of the page.

Single Page Application

The goal of this technique is to do as much as possible in the browser. It imitates mobile apps. You have to wait for mobile apps only once when they are installed.

Classic examples are Gmail and Google Maps.

This way of rendering is the front-end developer’s favorite. The rise of SPAs was boomed by web frameworks/libraries like React, Angular, Vue, and many others.



4. Server-side rendering with Hydration(SSR)

When people realized that sending empty HTML and a ton of JavaScript to the browser is not the best idea ever, a new way of rendering was born: Server-side rendering with hydration.

Instead of a CDN, you need a Server that runs code.

The Server generates the initial HTML - full page content and sends it to the browser together with JavaScript.

Before JavaScript is loaded people can still use the webpage: read content, scroll, and use links - just like a static page. But when JavaScript is loaded it provides interactivity to the page. The process of JavaScript adding interactivity to a static page is called hydration.

Instead of creating the page on the client, you now create the page twice, once on the server and again on the client.

Server side rendering with Hydration

This technique is a combination of a “Multi-page application” and a “Single Page Application”. A website like this has more than one page, but hydration is used on all.

Popular meta frameworks that work this way are Next.js, Nuxt.js, and SvelteKit.



5. Server0side generation with Hydration(SSG)

Servers cost money, many times you don’t even have control over how much. Because this was a problem for many reasonable people a new technique was born: “Server-side generation with Hydration (SSG)“. The core of Jamstack.

To get rid of the server, you “build” all pages to HTML and then put the HTML on a CDN. Just as in the case of “Server side rendering with Hydration”, HTML contains JavaScript that hydrates the page after it is loaded.

Since all this is static, you still need a way to provide personalization, data persistence, and all the things a server is capable of. For this you have APIs. Meaning your website uses servers that don’t respond with HTML but with some form of data like JSON, XML, streams, and so on.

Server side generation with Hydration

Popular meta frameworks that work this way are the same as before Gatsby.js, Next.js, Nuxt.js, and SveltKit.



6. Incremental static regeneration (ISR)

The most important drawback of the SSG is that you need to build the whole site every time one page needs an update. It would be nice if you would rebuild(regenerate) only the page that needs the update. That’s exactly what Next.js thought and implemented.

How this works is that they set a limited Cache life. While the page is cached, the server is not doing any work. When the cache expires, the server rebuilds the page.

Incremental static regeneration

One downside is that for the moment you need to use Next.js to go this route. Deployment and hosting options for this technique are limited.

Pros & cons: similar to Server side rendering with Hydration(SSR).

7. Deferred Static Generation (DSG)

A somehow similar take to ISR is the “Deferred Static Generation(DSG).”

When you have a lot of pages(say 100.000) - and you’re using SSG - the build takes hours. To fix the build time, they invented Deferred Static Generation, which means you can make some pages “deferred” and these pages will not be included in the build.

Any deferred page is built (generated) the first time somebody requests it. The nice thing is that a missing page is created only once, and is it not recreated over and over as in the ISR case. DSG is just an enhanced SSG.

One downside is that for the moment you need to use Gatsby.js to go this route. And deploy it on only Gatsby Cloud.

Pros & cons: similar to Server side generation with Hydration(SSG)

8. Islands

In most of the above techniques, the page is not interactive until the JavaScript is loaded and event listeners are added to the HTML.

A crazy problem is that this happens on all pages, including those that are only HTML! And in most cases, the size of the JavaScript that is loaded for no reason is bigger than the size of the HTML & CSS combined. This is bad for performance!

So there are a few possible situations:


A solution to hydrate only a part of the page is called Partial Hydration.

This idea was taken further by the Islands’ way of rendering heavily pushed forward by Astro. 11ty is another builder that has islands. An Island is a part of the page that is interactive by using JavaScript (e.g. a React component) while the rest of the page is static content.

What’s even nicer is that Islands can be lazy loaded and hydrate only when you scroll to them - further improving the performance.



9. Streaming SSR

Another way to tackle the problem of hydration is Streaming SSR. The idea is that you split the page rendering in chunks, when a chunk is ready on the server, it is sent to the client and it is also hydrated.

Streaming SSR

So this is just an enhanced SSR. This feature can be used with Marko, Next.js, or Shopify’s Hydrogen.



10. Resumability

Finally, there is a solution that solves the pesky problem of hydration.

Resumability is about pausing execution in the server and resuming it in the browser. But without having to replay and download all of the application logic.

The trick is that they serialize the server state in the HTML when the page is generated (SSR or SSG).

The result is that Resumability looks very much like static website rendering:

Static website

Event listeners (that are added by hydration without Resumability) are serialized also. For example, here we have a button, its click listener function is called handler_symbol and the function is inside chunk.js

<button on:click="./chunk.js#handler_symbol">click me</button>

Resumability implies the existence of a global listener that works like this:

In our case, when the user clicks the “click me” button the global listerner looks at that on:click button attribute. Then it loads chunk.js, and executes handler_symbol.

The result is that this type of website has a lot of small JavaScript chunks instead of big ones. Also, only the necessary chunks are loaded.




As I’m writing this, a new technique is making waves on Twitter, Server functions, a sort of RPC incarnation. Who knows what will be all the rage in a month? Things are moving too quickly for my taste.

For years the only solutions were static websites, MPA, SPA, SSG, or SSR. (Not that I’m complaining.)

The introduction of React Server Components opened the way for new techniques. Which then will influence even newer techniques.

Today we have a lot of good options for rendering a website. And more will arrive soon 😊. I just hope some of the mistakes will be figured out earlier.

As we can see from the new wave of techniques, SSR was a headache. It caused a lot of bloatware - why do you need to load a ton of JavaScript if you generate the page on the server?! And now everybody tries to fix that.

If the “next big thing” will be better or worse… we will see.

Want to learn more?