Deciding on the approaches and tools to use to build on web these days can be a challenging prospect. There are many different options available and they all claim they do it the best, and often in seemingly contradicting ways.
And strangely enough, they aren't wrong. There are many different types of experiences available on the web, from the simple business listing page to running AutoCAD in the browser.
But even knowing that the web is a wide and diverse place there is a desire to not have as many solutions as there are sites. And while it may be unrealistic to have a one size fits all, maybe there is a way we can rein in some of the confusion. By categorizing the techniques being used we have a way of talking about them, and can make meaningful comparisons.
The framework that has worked for me has been to look at this with 3 questions in mind:
- Where do you navigate?
- When do you render?
- How do you hydrate?
There is no single right answer. Different sites have different needs. So how do we make this more concrete?
We could look at UI frameworks, but they are a moving target, constantly updating and improving. By the time you read this article it will be out of date. Instead maybe it is best to talk about this by archetype (taking a page from Jason Miller's Application Holotypes)
Portfolio | Content | Storefront | Social Network | Immersive | |
---|---|---|---|---|---|
Holotype | Personal Blog | CNN | Amazon | Figma | |
Interactivity | Minimal | Linked Articles | Purchase | Multi-Point, Real-time | Everything |
Session Depth | Shallow | Shallow | Shallow - Medium | Extended | Deep |
Values | Simplicity | Discover-ability | Load Performance | Dynamicism | Immersiveness |
Routing | Server | Server, HTML Swap | HTML Swap, Hybrid | Hybrid, Client | Client |
Rendering | Static | Static, SSR | Static, SSR | SSR | CSR |
Hydration | None | Progressive, Partial | Partial, Resumable | Any | None (CSR) |
Example Framework | 11ty | Astro, Elder | Marko, Qwik, Hydrogen | Next, Remix | Create React App |
I've attempted to refrain from using acronyms but they are more compact in tables. If you come across any you are not familiar with there is a glossary at the end.
All these frameworks work perfectly well in multiple scenarios but just picked a couple of the options that fit.
This is just a small sampling. It is easy to see there are many options. Honestly, our classification can only go so far to indicate the characteristics of different tools you are choosing. But if nothing else perhaps this can serve as a starting point for conversation based on real needs to find the type of solutions right for your project.
Routing - The Backbone of the Web
The web was built on the the idea of being able to describe a location as a path. The URL is the driving force behind how all the web works.
In recent years, we've gotten more liberal with how we handle it but how we do is the most fundamental aspect of how our web sites and applications behave. Whole architectures and paradigms arise over how this routing is handle, as it is the foundation upon what all else is built. So this is why this is where we start.
How do you Navigate?
Server | HTML Swap | Hybrid | Client | |
---|---|---|---|---|
Code Entries | Multiple | Multiple | Single | Single |
Full Page Reload | Yes | No | No | No |
Preserves Client State Below Change | No | No | Yes | Yes |
Client-side Transitions | No | Yes | Yes | Yes |
Renders on the Server (after first render) | Yes | Yes | Yes | No |
JavaScript in the Browser | None | Low | Medium | High |
Example | Express | TurboLinks | React Server Components | React Router |
Multi Page App (Server Routing) vs Single Page App (Client Routing) is usually our first distinction. As old as Web Site vs Web App. But the nuance is that all sites are first served from a server. A server rendered SPA becomes an MPA simply by removing the <script>
tag.
As we look at the newer approaches in the center we are seeing this distinction be harder and harder to make. The key thing to note is these hybrids all are about leveraging the server more while maintaining client side navigation without reloads. Where they differ most is their ability to maintain client state below the change.
If there is any take away about routing it is that every time we've made a significant advance here, the whole paradigm of how we build apps changes. From initial server routing to client routing saw the dawn of SPAs. Push-state and History APIs enabled the isomorphic SSR SPAs we see today. And we are on the precipice of another one of these shifts today.
Resources:
Introducing TurboLinks for Rails 4.0 - Fabian Becker, 2012
Have Single-Page Apps Ruined the Web? - Rich Harris, Oct 2021
The Return of Server Side Routing - Ryan Carniato, Jan 2022
Remixing React Router - Ryan Florence, Mar 2022
Routing I'm not Smart Enough for a SPA - Taylor Hunt, Apr 2022
Rendering - Bringing HTML into View
For rendering, regardless of technology, the end results are the same. For consumers of your site to be able to see anything, it is rendered into HTML that can be interpreted by the browser and displayed.
So the whole question comes down to when. And this has many tertiary considerations, like availability of data, and ability to cache. High latency data due to infrastructure constraints or how effective caches are can drastically change the performance characteristics, but we can still make meaningful associations through this classification.
When do you Render?
Static (or Cached) | SSR (Streaming) | SSR (Async) | SSR (Shell) | Client | |
---|---|---|---|---|---|
When Render? | Ahead of Time | Server | Server after fetching data | Server And Browser | Browser |
When Data Fetched? | Ahead of Time | Server Request | Server Request | In the Browser | In the Browser |
TTFB | Low | Low | High | Low | Low |
FCP | Low | Low | Medium | Low | Medium |
LCP | Low | Medium | Medium | High | High |
LCP : FCP | LCP = FCP | LCP > FCP | LCP = FCP | LCP > FCP | LCP > FCP |
Hydrates for Client Interactivity | Yes | Yes | Yes | Yes | No |
Examples | HTML in an FTP server, 11ty | Marko, React 18, Solid | Next, Nuxt, SvelteKit | App Shell, Jamstack | Create React App |
A lot of the rendering story is about data availability, when we fetch has a huge effect on when we render. It isn't uncommon to split the work, rendering static parts on the server and rendering the rest in the browser after fetching data there.
Also important, since all web sites begin on the server, any server rendered site can also be cached if sufficiently static, and gain those advantages. At some point you are taking the hit, and so much of this comes down to how tolerable latency at different points.
There are a lot terms for different on-demand and stale-while-revalidating cache strategies like Incremental Static Re-generation (ISR, DPR) as these features have mostly been platform specific to this point. Expect to see a lot more interesting caching strategies appear as we explore rendering at the Edge.
We have compute so near to the end user, but it doesn't necessarily change the data latency story. Streaming mitigates part of the problem by putting content in front of the user as soon as possible but expect more aggressive caching strategies as we continue to try to get the best of both worlds.
Resources:
Async Fragments: Rediscovering Progressive HTML Rendering with Marko - Patrick Steele-Idem, Dec 2014
The New Frontend Stack: JavaScript, APIs, Markup - Mathias Biilmann, Apr 2016
Next 9.4: Incremental Static Regeneration Beta - Tim Neutkens, May 2020
Server Rendering in JavaScript: Optimizing Performance - Ryan Carniato, Feb 2021
Distributed Persistent Rendering: A New Jamstack Approach for Faster Builds - Mathias Biilmann, Apr 2021
Hydration - JavaScript Frameworks' Greatest Challenge
Hydration is the work done in the browser to get a server rendered app/page into the same state as if it had been client rendered - Michael Rawlings
Hydration has proven a challenge to do efficiently. And it is important as slow hydration gives the illusion of a fully interactive site when it isn't. We talk about things like Time to Interactive looking at page load metrics. But different techniques often defer work rather than address it, and measuring these things is still a work in progress.
It doesn't help that it isn't easy to talk about Hydration. We often talk about qualities of the solution rather than having a name for the approach. The reason is Hydration operates along 3 dimensions:
When do you Load?
Eager (Load) | Progressive (Idle) | Progressive (Visible) | Progressive (Interaction) | |
---|---|---|---|---|
When Load | On Page Load | Idle Callback | Intersection Observer | On Event |
Priority | High | Medium | Low | Low |
Need | Visible and Hydrated Immediately | Visible Non-Critical | Offscreen, Expensive | Non-Critical, Expensive |
Impacts | Time to Interactive | Load Complete | Scroll Lag | Input Delay |
What do you Bundle/Serialize?
None | Partial | Full | |
---|---|---|---|
What is Bundled | Nothing | Interactive Islands | All |
What is Serialized | Nothing | Nothing - Minimal | All |
Knows Server Only Code | Yes | Yes | No |
Routing | Not Client | Not Client | Any |
What do Execute on Load?
Replayable | Resumable | |
---|---|---|
Execute Components | Yes | No |
Restore State | Yes | No |
Run Client Effects | Yes | Yes |
Attach Event Handlers | Yes | Yes |
Execution Cost | High | Low |
Serialization Cost | Low | High |
What makes this more challenging is that only when putting them together in combination can we infer what impact these have on Time to Interactive(TTI) or First Input Delay(FID). Resumability has a low execution cost but high serialization cost, but that can be offset by being Partial. Similarly, being Progressive is more costly when execution cost is higher.
Most JavaScript Frameworks today are Eager, Full, and Replayable. This includes every SPA framework and meta-framework built on top of them to create SPAs. Finding ways to mitigate this ever growing cost is requiring a lot of creativity and new exploration. As with everything else we are seeing a shift back to the server, but not a return to what was.
Resources:
Maybe you Don't Need that Spa - Michael Rawlings, May 2020
Islands Architecture - Jason Miller, Aug 2020
Introducing Zero-Bundle-Size React Server Components - Dan Abramov & co, Dec 2020
Is Okb of JavaScript in your Future? - Ryan Carniato, May 2021
What is Partial Hydration and Why is Everyone Talking about It? - Anthony Campolo, Nov 2021
Why Efficient Hydration in JavaScript Frameworks is so Challenging - Ryan Carniato, Feb 2022
Conquering JavaScript Hydration - Ryan Carniato, Mar 2022
Hydration is Pure Overhead - Misko Hevery, Apr 2022
Conclusion
Well, more like this is just the beginning. Classifications can only serve as a guideline rather than the rule. If 2022 has shown anything so far there is a lot of innovation going on.
Tried and true solutions are expanding the range of their offerings. Static generators offering on-demand server rendering. Streaming is becoming a mainstream rendering technique.
Newer solutions are taking on all new territory especially in the area of hydration. Routing will likely be the battleground for the rest of the year as we learn how to better get the server involved.
This means a lot more options are becoming available to us. But that's not a bad thing. Not bad at all.
Special thanks to Dan Jutan and Michael Rawlings for reviewing, and Addy Osmani for the original inspiration and directing me back to Jason's Application Holotypes.
Glossary
Name | Description | |
---|---|---|
SPA | Single Page App | Client-routed single-code entry application experience |
MPA | Multi Page App | Server-routed multi-code entry site experience |
SSR | Server Side Rendering | Rendering HTML on the server on demand |
SSG | Static Site Generation | Rendering HTML ahead of time |
ISR | Incremental Static Re-Generation | Serve from a cache, while re-rendering in background as needed |
DPR | Distributed Persistent Rendering | Cache resource on first request, and serve from cache until invalidated |
TTFB | Time to First Byte | The time from sending the request until receiving the first byte of the response in the browser |
FP | First Paint | The time until the first browser paint event |
FCP | First Contentful Paint | The time until the first browser paint event that shows meaningful content |
LCP | Largest Contentful Paint | The time until the majority of visible content has has been painted |
TTI | Time to Interactive | The time until all blocking resources and JavaScript have executed |
FID | First Input Delay | The time from the first interaction is triggered to when the result is processed |
Top comments (17)
Thank You for making this publicly available.
"Too often, though, we don't see teams making that trade-off analysis, blindly accepting the complexity of SPAs by default even when the business needs don't justify it."
SPA by default - Hold.
It feels like this article is providing some "trade-off analysis" criteria that earlier were either implied or have been missing entirely.
In 2017 PPK got into some hot water with his perspective on "what is a web developer"—but I think it would be highly beneficial for any "web developer" to fully understand Rendering Patterns (formerly Rendering on the Web) and this article.
Partially to understand where one's skill set actually fits in (hopefully avoiding wielding a golden hammer) and why a diverse ecosystem of libraries and frameworks is necessary to offer approaches that are better optimized for varying sets of solution constraints.
Again, thank you for broadening web development education in this fashion.
Thanks for the write-up.
Just to tie what you saying to jobs and learning. It seems a bit sad to me that it is becoming more niche to make a particular type of "javascript website". If you want a particular type of job, which do you choose?
Right now, many people choose to learn a UI framework (React is top I guess) and a related application framework (maybe Next as number 1 for React), and that covers more of the large-scale and corporate side of the spectrum of javascript websites (storefront, social network, immersive). And I guess this is where you can more reliably find a job, in terms of number of positions and salary.
While SSGs and newer frameworks like Astro cover the other side of the spectrum (Portfolio, content). These types of "javascript websites" might fall into the freelancer, or agency worker, or small business type of jobs.
Then, there is a middle-ground that is less distinct. For example, smaller storefronts can built in a wider range of tech because the demands are lower.
Does that sound like a fair characterization of how the landscape is relating it to jobs?
As for learning, I find it hard to pick winners for next wave of established frameworks in this space. So, I just retreat to the backend or more predictable shores! I think in the long-run we all gain in innovation and getting a faster web, but in the shortrun it leds to feelings of insecurity and uncertainty for devs.
What do you think in this regard?
Mostly. But its why there is a rush to the center. The problem is everyone is just choosing React whether its good for them or not. You'd hope Astro/Marko/Qwik would be getting the Lions share but they aren't. SSGs in React etc..I focus a lot on the future and the right tool for the job but I don't think that reflects in the job market. There we have companies having retired legacy all trying to use React.
It's a strange one. If all you have is a hammer, you treat everything as a nail! 😀 Let's hope that things pivot soon.
What‘s kinda sad is that in these small business type of jobs that you are describing, the industry standard still seems to be WordPress which most small agencies would rely on even if many sites could benefit a lot from using Qwik, Astro or 11ty with some headless CMS.
For me it seems that it still will take a while till there will be a true shift. What do you think about that?
I think some established agencies are WordPress shops. It depends on how progressive they are, and what customers ask for, if they shift. I think freelancers have more freedom to choose.
I guess a lot of the resistance comes from people wanting to choose a safe bet. Some folks dont want to learn 4 different frameworks for delivering the best type of website. This leds to the same choices being repeated. Really, they should focus on the suitable niche, rather than accept projects where WordPress is not the best fit.
You don't really need JavaScript in title. You could also include Jekyll, Hugo, and newer ssr like Laravel + http caching as an option
Those technologies are primarily Gen 0 technologies despite the fact that they are in modern use.
Given the various classifications given here Gen 3 solutions are also MPAs but for the time being they will be heavily reliant on server or edge side JavaScript especially if they are aiming for "resumability" until such point where code generation can replace the server side code with something else.
MPA, "Server-routed multi-code entry site experience", doesn't imply pre-2010 style dynamic server side rendering of (just) HTML.
Laravel and HTTP caching are new?
I am thinking of the turbo/hotwire/livewire/liveview family of ssr. I don't know how old livewire is.
I would in concept describe those as server rendered SPAs requiring a continuous high quality, high bandwidth, low latency connection. As per navigation they classify as a "hybrid" - a complex version of Turbolinks - where client state is managed server side.
The term SSR keeps being used in a variety of ways.
In many contexts it implies firing up client side JavaScript on the server to generate markup and state to be sent to the client which then takes it and runs with it.
I personally avoid using "SSR" to mean "Classic SSR".
«The Rails Hotwire» reference here should probably be «Hotwire Turbo Streams». Since Hotwire is written in TypeScript and can run with several types of backend (like Django, with turbo-django).
«Turbo Streams deliver page changes over WebSocket, SSE or in response to form submissions using just HTML and a set of CRUD-like actions.»
turbo.hotwired.dev/
Based on the this "NEW MAGIC" was renamed to Hotwire in December 2020. I have no doubt that the implementation continued to evolve.
The point really is the order of emergence:
Thanks for this, it shed light on many things.
I used to struggle to know which tools, methods and techniques to use. Now I have a bit on an idea what to think of when building sites.
Awesome article these are some really advanced concepts.
Awsome article.
did u discover it? lol
Anyway Thank you for making this article.
I would add something that places traditional CMS's into context because many websites are still using traditional CMS's.