This is a living document. Come back often to get updates.
Frameworks and rendering techniques
Remember that if an implementation is working for you and it solves yours (or your customer's) business problems,
you do not need a migration or a new piece of tech to maintain.
It is a mistake to adopt a new architecture paradigm or set of principles, if they're not compatible with your solution.
Client-side (at runtime)
Server-side rendered (universal or isomorphic)
Static generation (at build time)
Full hydration (full app bootstrap)
Application type (based on routing strategy)
Single Page App
Multi Page App
Application type (based on use of libraries, open standards and cloud technologies)
Static Web App
Progressive Web App
Functional Web App
Please note that these types are not mutually exclusive. They are just ways of classifying an app, depending on
the predominant characteristics.
We are going to focus on frameworks that are considered new generation typically known as `meta-frameworks`, and that prioritize
This is why we're not going to be discussing client-side rendering, except to mention universal rendering techniques,
that are used to render a client-side and a server side version of the HTML, and then transfer state from the server to the client, during the bootstrapping of the application.
I explain universal rendering with a diagram and the impact to some of the most important performance metrics at that time, in this slide deck from 2020.
Why the shift from client-side to SSG and/or SSR?
It feels like frontend development has been going in circles for a very long time. From concatenating all static assets to send them in huge files in one request,
The most significative change of paradigm in the last 5 years, has been a resurge of moving the rendering of the HTML from the client, a strategy introduced by client-side frameworks promoting the development of Single Page Applications, back to the server...But why?
The answer is simple: it's all about performance. About user experience. About speed. And obviously, about avoiding end-user bounce and facilitate conversion.
Client-side rendering results in bad performance scores
The speed at which client-side rendering happens is affected by way too many variables, as I will explain later in the user experience section.
that may be impacted by the device and browser capacity -or lackthereof- to run the code.
You can find more information on important metrics for good user-experience, visiting the Core Web Vitals page.
The birth of the meta-frameworks
With the general acceptance of the idea that client-side rendering is sub-optimal for performance, a new breed of frameworks emerged.
Meta-frameworks are a descriptive term for those frameworks that implement some of the optimizations described above, and are typically isomorphic, implement
file-based routing, and serialize state to transfer it over the network during a process we will later describe in-depth, called re-hydration -also known as hydration.
What is isomorphism?
What is serialization?
Serializing is the process of converting an object into a format that can be stored in a memory buffer, or sent over the wire using standard protocols. For example,
Static Site Generation - aka SSG
Static Generators typically generate HTML from markdown, at buildtime.
The result are therefore static pages that are usually cached at CDN level.
to inject state, to enable routing or some dynamic features.
Frameworks supporting server-side rendering or universal rendering, typically generate a server-side and a client-side
version of the render. The server-side version is the static HTML, oftentimes cached, and the client-side version is the result of bootstrapping the application.
State is transferred from the server-side to the client-side, during the bootstrapping or initialization of the application, with considerable overhead. This process is called hydration.
Hybrid rendering strategies combine static generation with server-side rendering techniques and total or partial hydration.
All the afore mentioned rendering strategies require some sort of transpilation or compilation step.
HTML-First frameworks, however, promote an HTML plus web platform APIs strategy, following the progressive enhancement approach.
image caption: static site generation flow - buildtime
image caption: server-side rendering flow - on the fly
Hydration is the process of transferring serialized state and dependencies from the server to the
static HTML in the client. The HTML can be generated with a static site generator at buildtime, on the fly -meaning because of an event took place on the client-side, that triggered a request for additional HTML- or
was pre-rendered and sent from the server -or a caching layer-.
There are different methods to hydrate -or re-hydrate- static HTML: it can be fully hydrated, progressively hydrated or partially hydrated.
This is the most common method, used by most frameworks that support server-side rendering. When full hydration is implemented, the whole application is
bootstrapped, typically during pageload, destroying and recreating the whole Document Object Model -DOM- tree, while state is evaluated and/or data is transferred.
Progressive hydration proposes to bootstrap parts of the tree independently, or progressively over time. This technique helps
Partial hydration is the concept behind the Island Architecture approach,
pushing the idea of progressive hydration even further. It suggests that we define areas of interactivity of our horizontal split,
Streaming is a technique through which the server sends -read streams- chunks of data sequentially, -individual snippets of HTML- to the frontend, to render over time.
Because we can decide what chunks we want to stream earlier, typically those that will be rendered over the fold, we can assume that we will get better Lighthouse and Web Core Vitalsscores.
This technique is also implemented in combination with edge computing or edge functions for composition, at the edge of the network, to return a full page back to the CDN for caching and to the client-side to render the whole view. With Node.js as a runtime, we
can leverage its Stream API, to implement this technique.
Future versions of this site will include links to code samples, to illustrate this approach.
image caption: SSR streaming
Node.js Stream and Web Streams API
Although Node.js streams can be converted to Web Streams API, to stream data from the server to the client -not in the oppossite direction, though-, both APIs are not the same API.
As briefly mentioned, `Island architecture` is an implementation of partial hydration that uses the analogy of isolated regions of a page or view, that could be either static or dynamic.
One of the characteristics of Island Architecture that makes it a special form of partial hydration, is that there is no need for a shell. Each micro part is effectively independent from the others. It is basically the page itself that identifies the placeholders for those
No matter what type of hydration we're talking about, a not so obvious downside when we're not familiar with the internals of the mechanics
is duplication. When we hydrate a page, we are literally re-rendering it. So to hydrate we are duplicating the render -meaning the DOM tree-, the code and even sometimes the data and the state, with a consequent degradation of performance or even a glitch, where data on the client-side updates in way that is visible for the user.
Even when partial hydration is a lot less impactful to performance than a full client-side boot up, it can still degrade it. And it definitely will have a certain cost of execution, even when minimal.
image caption: island architecture
What does `pick up where the server left` exactly mean?
It means that because those assets are sent in a serialized form -meaning, as already explained, converted to a format that can be transferred over the wire-, the client can resume the execution of the code, and the state of the application, without having to re-render the whole view.
More on Resumability
These are some of the frameworks or libraries, implementing or exploring resumability:
`Wait a minute! You didn't mention my favorite framework! Does it mean you don't recommend checking it out?`
Short answer: NO! There are dozens of frameworks! Use the one you like best! They don't fit all on this site!
I'm not focusing on the many amazing frameworks that do CSR or SSG, only. Some of them implement universal features, and are evolving towards a hybrid and mode modular, approach, but that I will discuss in future updates.
I'm also not specifically mentioning frameworks for mobile development, because they're out of the scope of this site, at least for now. But if you're interested in learning more about mobile development, I recommend you head to Flutter, NativeScript or Ionic, to learn more.
Please note that supporting a pattern is not mutually exclusive with supporting another. Frameworks are well known for promoting a certain
architecture, to be implemented without a lot of effort -given they are designed with a rendering model in mind-, but you can potentially accomplish a lot more
by experimenting. Also modern frameworks tend to be actively developed, and add more features over time.
Microfrontends and web standards: Web Components
Web Components are actually a set of three different standards that allow developers to create reusable components, in an object oriented way.
These three main technologies are:
Custom Elements, a collection of web platform APIs that can be laveraged to create a custom HTML element and define its behavior.
The Shadow DOM, another set of native APIs that enables a shadow or virtual object model that is attached to the custom element, and encapsulated away from the main document object model functionality.
And finally, HTML templates, a capability to write markup that is not immediately rendered in the page, and can be used as a blueprint to any custom element.
Because Web Components are basically part of the Web Platform native APIs, using them reduces the amount of code we need to write, compile and ship to the browser to make an application work, while enabling interoperability with other APIs available to the browser.
Lit is a lightweight library providing all necessary modules to create and instantiate Web Components, in a declarative way. I personally love its simplicity.
Web Platform APIs and Micro Frontends
I have already mentioned the benefits of sticking to browser native APIs, and Web Components are not the only set of API's that are a real good fit to composable frontends.
We can even wrap native HTML elements in custom elements, to extend their functionality, and we can also use the Shadow DOM to encapsulate any additional capabilities as a product of the enhancement, so it doesn't interfere with the native element or other native elements in the page.
More on Progressive Enhancement
We also had to support cross-browser compatibility when the engines were a lot less mature and the standards were not as evenly implemented as they are today, across many of them in our support matrix.
So we either implemented graceful degradation, meaning we implemented all the features and then we removed -or blocked somehow the execution with conditionals- of some of the functionality where unsupported, trying to preserve the user experience as much as possible, or
progressive enhancement, where we started with the bare minimum to ensure requirements were met, and added enhancements progressively where supported.
We implemented tons of device and agent detection for that -blocking the critical path- ouch!
Today, I feel there is a lot less of that, but I still believe progressive enhancement is a good practice to preserve the user experience, and when it comes to choosing the framework or technology stack for our frontend, thinking of an HTML-first approach, is a good way to go.
Microfrontends and events: the Streams API
The Web Streams API is a standard Web Platform API, that can access a response body sent over the network via the HTTP protocol, when it has been exposed as a readable stream, so a developer can lock a reader. That would be a readable stream, but the API allows us to create writeable streams, as well.
The interesting aspect about this API, is that it simplifies the process of consuming raw data on the client-side, directly from the network, without having to implement a buffer.
This article by Thomas Steiner can help you understand streams in the browser more in-depth, and has information about browser support for the time the article was written.
Other browser APIs
As I described in this article, two years ago, there are quite a few browser native APIs that can helps us compose beatuful and consistent
user interfaces, without overhead, and that can be laveraged by independent teams working in decouple frontends.
I will elaborate more on each in future iterations or when I start release code samples.
History -especially important to build a custom router and the URL strategy for UX-
Drag and Drop
Service Workers are a great capability to implement progressive enhancement, out of the box and from the get go: if a user visits a site implementing service workers with a browser that doesn't support them, no functionality will be be lost, and nothing will break.
But what exactly are they and how do they work?
Service workers are a complex piece of technology and I can't condense all they do in a simple paragraph. The idea is to extend this site with practical examples and even challenges to get started with technologies, but for now, let's just say that service workers are
a technology that runs in its own thread*, has its own lifecycle, and it's built to intercept network requests and responses, and cache them, so they can be served offline, or even when the network is slow or the connection goes intermitently offline and online.
Service workers are a great aid to implement e2e event-driven architectures, and particularly helpful is its implementation of the Cache interface that, unlike the HTTP caching mechanisms that are header based, can be programmatically controlled and has access to
the worker's scope running in its own thread, but also has access to the main thread of execution, so it can be used to cache any kind of data not only network responses.
This is also useful to compose micro frontends from cached data, since we can either precache -cache on install- or implement cache on demand, at runtime, caching data from network responses from other sources, or from browser storage, like IndexDB or localStorage.
This takes us to another type of rendering, that I left out of the previous hydration section because it involves service workers, and I needed to explain them first. If isomorphic describes an implementation that runs both client-side and server-side, trisomorphic
as you may have deduced by now, involves a third dimension and that is the service worker thread. We can use the service worker's caching interface to store full chunks of dynamic HTML to be integrated when the user demands a specific view, or in response to an event or change of state. Basically, we can serve a specific content from the service cache, when a condition is met.
One framework that is entirely designed around the concept of HTML-First, custom elements and
pogressive enhancement, that I really like, is enhance.dev.
This framework is very interesting because out of the box, it needs 0 compilation. It is JUST HTML -custom- elements.
to enhance certain elements and make them more reactive or dynamic.
Enhance.dev also promotes the Functional Web App approach via the Begin CLI, a tool designed
by the same creators, to deploy pure functions to a serverless context, and that's powered by an infra as code mechanism.
I will provide extended information on the concept of PWA -Progressive Web App- and FWA -Functional Web App- approaches, in future iterations of this site.
in the server.
Not only you will obviously need a server to run your code, but you will need to strategically design your app so whatever is rendered in the server as static HTML, becomes later dynamic using the hydration techinques described above.
Some features, like analytics or user tracking, involving cookies, etc, will definitely need to be run client-side.
Web Workers are of great help to run specific code in the browser, particularly code that needs to load and run early and has the potential of blocking the main thread, without actually blocking the main thread.
Learn more about Web Workers here.
image caption: workers
Can I use?
The Web Platform standards and APIs are continously being developed and integrated to browser engines. Browser support for standards is more consolidated now that it was a decade ago, and still,
some engines lag behind, while others take the lead, depending on the capability.
caniuse.com has been a reliable and up to date source of support information for a very long time.
This website uses a technical cookie to save your cookie preferences in your browser. Additionally, it uses Google Analytics to analyze the traffic.