Rendering Patterns (Architectures) of Your Frontend
Here you can see main architectures of Frontend applications and main frameworks that help to build this architecture.
When you plan to build some web application, most probably you will work on the Backend and Frontend sides of the application. In this post, I want to describe the main approaches to build the FE of your application.
Classical Server Side Rendering
It is the oldest way of getting your FE done. With this approach, you create some simple static HTML files, that don't need much interactivity.
Main characteristics of this approach:
suitable only for static pages
not much interactivity is possible
great for SEO
page-load is fast
So, if you need to create just a couple of static pages, this approach is the best for you.
Frameworks: can be implemented without any framework, because pages are assumed to be not much interactive.
Classical Client Web Application (SPA)
The popular name for this approach is SPA (single-page application).
In this case, your user will download some empty HTML page and a huge amount of JS files. Once all the files are downloaded, the browser will execute these files and then the user will see the application and will be able to interact with it.
Main characteristics of this approach:
can be very interactive
easy to implement
pretty bad for SEO even now, but earlier SEO didn't work at all
long time to get the first interactive render
if the user has a slow network or a weak client device - such an application can be very slow
This approach can be ok if you know that your users will have good devices and stable internet connection, and you don't need to optimize for SEO.
Frameworks: by default when you compile your React, Angular or Ember into one file, you get SPA.
Server-Side Rendering with Hydration
If you need to optimise your web application for SEO, you will need to get most of the content already in your HTML.
In this case, when the user makes a request for the page, your BE builds the HTML, returns HTML & JS to FE, and on FE we immediately see the content and once we execute our JS, the page starts to be interactive.
Main characteristics of this approach:
you are getting much faster first paint because you already download html that should be rendered
but you will get a slower response from BE because it needs to render the HTML first
time to interact is the same as with a single-client application (SPA)
This approach is suitable for cases when you want to create pages that support SEO optimization and should be interactive like SPA.
Frameworks: a classic example here is Next.js, but currently many frameworks can work in different ways, and I could least the same frameworks for different approaches.
Prerendering pages
If you have many pages and they should have similar designs, it can be hard to do it with the 'Classical SSR' approach. With 'prerendering' you can create and develop something similar to SPA, but during build time you will get static HTML pages.
Main characteristics of this approach:
getting faster first paint (as for Classical SSR)
you don't need to wait for the BE to render your page, so we get a faster response time from BE
doesn't work for dynamic content
doesn't work for a site with tens of thousands of pages or if new pages can be accessible with a new URL
Frameworks: a classical example is Gatsby. But you need to search for some Static Site Generators. Examples of Static Site Generators are Jekyll, Hugo, Gridsome, Eleventy, and Pelican. Currently, Next.js and Gatsby can be configured to work with different approaches.
Progressive hydration
It is one of the possible improvements of SSR with Hydration. In this case, your BE generates the whole HTML page and returns it to the client. But on the client side, we hydrate not the whole page, but only some part of it.
With progressive hydration, we can reduce the 'time to interactive' and make the JS files smaller. Such an approach gives you the opportunity to set up the priority in which hydration should happen. For example, you can hydrate only visible elements on the page or even make priority between visible components.
Main characteristics of this approach:
reduced bundle size
can on-demand hydrate components that are not critical for the user
promotes splitting your code into pieces
This approach is suitable for applications, where you can determine strong priority between the parts of the page. For example, if your page fits the screen size and you don't know where the user will click first - you won't be able to understand in which priority hydration should happen.
Frameworks: Fresh, Bun.
Streaming Server Side Rendering
One more option for optimizing SSR is Streaming Server Side Rendering.
With this approach, you can render the page in chunks and return parts of your HTML once it is ready.
Some time ago browsers learned to render page part by part based on node streaming. Our BE won't generate the whole page, but instead, it will split the whole page into chunks and render them one after another. It allows us to optimize the load on BE and network while giving us faster paints and time to interactive.
Main characteristics of this approach:
we get a faster response time from the server
smaller load for the server itself
can be bad if you have large pages with lots of HTML, you will need to render lots of parts and it can take time
Frameworks: Spectrum, React (renderToNodeStream).
React Server Components (RSC)
Another tool to improve SSR is server components in React. With this tool, React renders some components on BE and returns to the client a special piece of code that later will be transformed into HTML.
If you are thinking about what can be a server component - you can think about some 'data components' that retrieve data from BE and render it. If you handle such 'data components' on the BE side, it will reduce the number of requests to the BE (faster interactions) and if you use some libraries to transform the data before rendering (for example, rendering a date), you can also reduce the size of the JavaScript bundle.
Main characteristics of this approach:
suitable only for 'data' components
returns '0' JS to the client
renders on the BE side
you have total access to the BE code
you don't have access to client-side events, state and effects
RSC was introduced in 2020, but the actual support of it you can see only in Next.js 13.4 (which was released in the summer of 2023). So, it is a relatively new approach and there is not much experience in production.
Frameworks: React (since it is React Server Components).
Islands Architecture
It is also one of the possible ways to build an SSR application and is somewhat similar to Progressive Hydration.
With Islands Architecture you split your pages into two groups - islands and static content. In this case, if some part of the page doesn't need interactivity, it can be static content. While components, that should be interactive - can be islands. For example, on a blog page, you can see static content - blog text, header, and islands - image carousel, social media buttons, etc.
So, on the BE you render the whole page with some placeholders for your islands. On the FE you get JS bundles for each of your islands and do hydration only for them. In this case, your bundle size should be smaller and the speed of hydration should be also improved since you will hydrate not the whole page, but only a small part of it.
The main difference between Islands Architecture and Progressive Hydration is the flow of hydration. With Progressive Hydration the page itself will start hydration and most probably it will hydrate one component after another. While with Islands Architecture your islands are independent and can be hydrated asynchronously.
Main characteristics of this approach:
reduces the size of the JS bundle
reduces the time for hydration on FE
great for SEO
forces you to split your code into components, that leads to reusability and maintainability
This approach is suitable only if your page has a lot of static content and not many islands of interactivity. Because, if you make an online editor or a social media where most of the page is interactive - it will need to hydrate hundreds of islands.
Also, this approach most probably requires you to switch to a new framework, since most of the already stable frameworks didn't implement suitable functionality.
Frameworks: Fresh, Astro, Marko, Eleventy + Preact.
Summary
Previously, I've described all the major patterns to render the front end of your application. Based on the type of application you can select what is the most suitable for you.
If you have a few simple static pages, you should go with Classical Server Side Rendering. Or if you have many pages, but they are pretty static too - Prerendering pages is the way to go.
If you are building an application, that doesn't need to support SEO and you know that your clients have stable internet connections and modern devices - you can go with Classical Client Web Application (SPA). For example, you have some analytic tools for brokers, online editor, etc.
Otherwise, if you need SEO and interactivity - you should implement Server Side Rendering (SSR). And based on your needs you can decide if you need any of the possible optimizations.
Hope you enjoyed the material and if so, subscribe and stay tuned for more useful content.