DEV Community

Cover image for Patterns for Building JavaScript Websites in 2022
Ryan Carniato for This is Learning

Posted on • Updated on

Patterns for Building JavaScript Websites in 2022

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:

  1. Where do you navigate?
  2. When do you render?
  3. 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 Facebook 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

Image description

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

Image description

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

Image description

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)

Collapse
 
peerreynders profile image
peerreynders

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.

Collapse
 
robole profile image
Rob OLeary • Edited

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?

Collapse
 
ryansolid profile image
Ryan Carniato

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.

Collapse
 
robole profile image
Rob OLeary • Edited

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.

Collapse
 
vincentdorian profile image
Vincent

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?

Collapse
 
robole profile image
Rob OLeary

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.

Collapse
 
codewander profile image
codewander

You don't really need JavaScript in title. You could also include Jekyll, Hugo, and newer ssr like Laravel + http caching as an option

Collapse
 
peerreynders profile image
peerreynders • Edited

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.

Collapse
 
davedbase profile image
David Di Biase

Laravel and HTTP caching are new?

Collapse
 
codewander profile image
codewander

I am thinking of the turbo/hotwire/livewire/liveview family of ssr. I don't know how old livewire is.

Thread Thread
 
peerreynders profile image
peerreynders • Edited

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".

Thread Thread
 
redbar0n profile image
Magne

«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/

Thread Thread
 
peerreynders profile image
peerreynders

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:

  • Phoenix LiveViiew
  • Laravel Livewire
  • Rails Hotwire
Collapse
 
maadmat profile image
MaadMat

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.

Collapse
 
andrewbaisden profile image
Andrew Baisden

Awesome article these are some really advanced concepts.

Collapse
 
metafox0114 profile image
MetaFox

Awsome article.
did u discover it? lol
Anyway Thank you for making this article.

Collapse
 
codewander profile image
codewander

I would add something that places traditional CMS's into context because many websites are still using traditional CMS's.