The 90's are back in style
Time to dust off the ol' nostalgia goggles
Ah the 90’s, when Friends was back in its hay-day and Spice Girls CDs were selling like hot cakes. The web was so much better back then with its static, hand-written HTML files. CSS was a nightmare to work with having just been introduced in 1996, and JavaScript was nothing more than a twinkle in Brendan Eich’s eye.
Or at least that’s the story I’ve been told; I’m just young enough that I don’t remember that much of web 1.0 and just old enough that I can pretend I lived through it.
Even though I was being coy there was is a kernel of reality; web 1.0 had a certain Je ne sais quoi about it. The blandness of the pages and the lack of any dynamic features made any content you put on it feel naked and exposed. To give some context here, multi-page-application (MPA) wasn’t a term because it was the default and no alternative existed; dynamic content had to be loaded, parsed and compiled to raw HTML on the server before the client received any HTML. The lack of any bells and whistles and high barrier to entry forced you to question if you reeeeaalllyyy needed to share that crab salad recipe with the world or not.1
Once Ajax got mentioned by Steve Jobs at Apple’s WWDC though things took off and Web 2.0 was born. With it came dynamic sites, which could use JavaScript and DOM2 manipulations to change the content being rendered on the screen in real-time. Now, instead of needing to refresh the page every time you wanted to load new content, you can simply watch new data being streamed instead.
As with all new technologies, people got a little… carried away. See, rather than sprinkling a little JavaScript here and there to add dynamic content where needed, people started sprinkling a little HTML in their JavaScript code and, well, stopped writing actual HTML.3 I don’t want to get too in the weeds here so I’m going to outsource to other great resources on the web about this:
- Mac Wright’s blog post about SPA fatigue
- Emil Björklund’s “Still Not Dead”
Modern problems require modern solutions
One of the unfortunate realities of web development is that these new JavaScript-focused frameworks were providing a much better developer experience than previous solutions. I don’t know about you but needing to write in three different languages in separate files littered throughout my code-base never felt that fun.
That’s kind of a big part of why adoption was so massive. Writing JSX is actually pretty natural - Vue and Svelte even more so.
So rather than trying to regress away from JavaScript-based projects people decided to make the JavaScript smarter. Take NextJS for example, which is a meta framework built on-top of React. Rather than making the client suffer the consequences of needing to run a bunch of JavaScript before being able to see content, Next pre-renders pages by default to produce HTML files with actual content on them! If that’s the case though, then how does the content get updated? Well, Next solves this by first sending the HTML, followed by JavaScript which then hydrates the page with the dynamic features of modern web development - client-side routing, content updating without page refreshes, etc…
Great! So web development is solved? Can we all go home now? Well… not exactly. To demonstrate why I’m going to use this very website as an example. Initially this website was written in SvelteKit, which solves the SPA problem in a very similar way to NextJS. While testing the website load times on my laptop and even my android phone it was very responsive and loaded quickly, so I assumed I would get an easy 90+ in Google’s Lighthouse performance score. Google, however, begged to differ:
Okay, it’s not like this is a horrible result. In fact in almost every way this is a great result. The only thing pushing the score down so much is that pesky “total blocking time,” which is the time delta between the “time to interactive” metric and the “first contentful paint” metric. This, in fact, is one of the big issues of the solution NextJS and SvelteKit use. While pre-rendering the page to static HTML with hydration tacked on later helps the “first contentful paint” metric a lot, it severely worsens the “time to interactive” metric. So in essence these meta frameworks leverage SEO, meta tags and static content at the expense of initial interactivity.
To be clear, this is not inherintly a bad idea and in fact this solution is a great compromise in many situations. But as with most software solutions it depends on the application and its business model what metrics are most important to optimize. In many situations, the trade-off NextJS works really well (imagine a content platform like twitter which needs meta tags in the HTML sent to the client for link previews)
Making things old-school again
So we’ve seen what trade-offs we need to make if we want to adopt the typical modern JavaScript UI frameworks. But what if our content isn’t that dynamic? What if we know, ahead of time, that solutions like NextJS and SvelteKit are overkill. At the end of the day, I just want to write a static blog website while avoiding hand-writing HTML and copy-pasting my header to every file. That shouldn’t be so hard, right?!
So if we don’t want to use something like NextJS or SvelteKit, what alternatives exist? We had GatsbyJS, which kinda inherited the worst of all worlds by shoving Graphql into their stack. Then there’s eleventy (or 11ty, I’m not sure which one is more kosher) which is pretty great, but it uses Nunjucks as its templating engine, which is a tool that’s almost older than me. Hugo is absolutely amazing and has a thriving community but it’s very far-removed from normal web development and is more targeted at SquareSpace/Ghost/WordPress people who don’t want to write a lick of HTML, CSS or JavaScript.
I don’t know about you but none of those sound all too appealing (except maybe Hugo, if I found a starter template that perfectly matched what I was going for). Then, like one of those cheesy “Has this ever happened to you?” advertisements, someone on the interwebs mentioned Astro and I immediately knew I found what I was looking for.
So what does Astro do that’s so revolutionary? In short, it gets out of my way while still supporting me in all the ways I need it to. Dynamic routes that get compiled to static routes at compile-time? Check. Modular React-style components but that compiled to static HTML? Double check. Vite? Check with a tip. Native support for markdown and importing images, svgs, and dynamic JavaScript components from existing frameworks? Triple check with a gold star.
It was so easy refactoring my SvelteKit website to Astro that I had to double-check mutliple times that the builds weren’t producing any errors4. I want to make a dedicated post talking about how exactly Astro works and what it did differently that made it so exciting, but just to prove my point (and put my code where my mouth is) I re-ran the Lighthouse benchmark through web.dev and, well, the results speak for themselves:
The Grass is Always Greener on the Other Side… Always
Okay, it can’t all be sunshine and rainbows, right? Yes, I do miss out on a few important things that SPAs do really well. The main one being that MPAs can’t store state while changing routes without resorting browser tools like LocalStorage, WindowStorage and Cookies. This is because it doesn’t implement client-side routing and every page loaded is a new HTTP GET request being sent to the server. One of the problems I struggled with on my personal site was retaining the toggled dark mode through new page loads. My solution is a bit hacky and uses an inlined script tag to read LocalStorage as soon as humanly possible to avoid an initial flash of the wrong colorscheme on page load.
That being said, there may be better solutions coming like Cloudflare workers, cookies + server-side rendering or other techniques. Sufficed to say, MPAs aren’t perfect, but they solve the issues of SPAs so well that they should be seriously considered for any site that relies on a lot of static content.
As always, the code that generated this website is free and open source and you can find the code on Gitlab
Footnotes
-
If you do actually know some good crab salad recipes please do make sure to contact me. Sharing is caring. ↩
-
The DOM is an acronym for Document Object Model - it’s simply the abstraction of the HTML document being rendered on the browser, and is a literal JavaScript object you have access to when running JavaScript on the browser. ↩
-
Before you bring your pitchforks out I actually really like JSX syntax. ↩
-
PS: they weren’t. ↩