Website Refresh 2025

February 21, 2025

I've been wanting to write again recently, but I also find myself procrastinating a lot instead of writing. This month, my main source of procrastination has been "fixing" my website - by which I mean breaking it on purpose by upgrading things, and then fixing it again. This is somewhat of a cliché for anyone who maintains their own site. Why actually write when you could just tinker with things that aren't important to anyone except you? In an attempt to get something valuable out of the process, here's a short description of the changes I made, if only for my own benefit when I inevitably forget what I did.

Step 1: pnpm

The pulling of the long string began when I opened VS Code, intending to write something for my blog, and noticed that the copyright date was outdated. It was hard-coded to 2024. Since the site is built in Gatsby, there's no reason why I couldn't alter the footer code to get the current year instead. So I did that, and it was of course very easy. But did I stop there? No, I did not. I thought "Hmm, it's 2025 now, maybe I should stop using an odd combination of yarn and npm to install things and get with the times." So I switched my package manager to pnpm.

I should note that I've used pnpm in other projects, and I like it, it's great. The concept of not actually having to have a hundred versions of the same JS library stored in various node_modules is a good one. However, switching to it from yarn without stopping to think about how that might go is not a good idea. The crucial, and in hindsight, obvious step that I missed was ignoring my yarn.lock and just reinstalling the project dependencies from package.json with pnpm. Literally 1 minute of searching after the fact showed me that I could have simply imported the contents of the lockfile into pnpm using pnpm import.

Having not done this, and not having aggressively pinned the minor versions of my dependencies in the project definition, when I reinstalled the dependencies, things upgraded. Things upgraded and broke.

Step 2: Gatsby nightmares

When we were working on a recent project together, and I suggested Gatsby, my friend Sim alerted me to the fact that it was the second-most-disliked tool in the JavaScript ecosystem according to the State of JavaScript 2024 survey. I was surprised by this, as I'd not really had any issues with it in the past. However, the next few hours made me truly understand where this sentiment comes from.

I won't bore you and my future self with too much detail here, but post-reinstall my site would not build. I tracked this down to an issue with an outdated Gatsby plugin I use: gatsby-remark-relative-images (an unmaintained plugin for... something to do with relative image paths). This plugin requires a very specific version of cheerio, but doesn't specify that version in its own package.json. What is cheerio? I'm not sure, but it involves HTML parsing. It turns out that it had changed how it was exported in one of its releases in a way that broke the plugin.

The upside of this problem was that I learned how to use pnpm's overrides feature to pin the version of cheerio to 0.22.0 for that dependency only. It looks like this:

// package.json
 "pnpm": {
    "overrides": {
      "gatsby-remark-relative-images>cheerio": "0.22.0"
    }
  }

The downside was I spent about 2 hours debugging the issue, time that I was intending to spend on writing.

Step 3: More Gatsby nightmares feat. hydration

However, this wasn't the end of my woes. After fixing the first issue, my site would build, but on load would flash up with the content and then show an empty screen. Even better, I only discovered this issue when I deployed it to Netlify, since the build worked OK locally. But why?

As is the fashion, the way Gatsby works by default when it receives a request is to load the static HTML page it has generated in its build step, followed by hydrating a React app to allow interactivity on the page. This is billed as a "static site generator enhanced with React Hydration." Never mind that my website is completely static and doesn't need to hydrate a React app.

In its infinite wisdom, by default Gatsby doesn't use the same workflow in development as it does in production. The gatsby develop command used in dev does not do the static HTML generation step, instead using a webpack server (I think?) to serve the pages. This means you get hot reloads and all that good stuff, but it crucially also means that any hydration errors are not seen in development.

Hydration errors occur when the static HTML loaded doesn't match the HTML generated by React. This makes React upset. Sometimes it can recover from a hydration error by fully re-rendering the page, but sometimes it can't.

To debug these issues, I discovered that setting Gatsby's DEV_SSR flag forces pages to server render on dev, leading to the hydration workflow and at least allowing the errors to be seen locally. In my mind, it's not very intuitive that you'd need to turn DEV_SSR on to see a problem that is occurring between a pre-built HTML page and the React hydration step, but here we are.

My hydration errors were caused by two sets of problems. The one that was completely bricking the site on client load was fixed by following a random person's suggestion on a GitHub thread somewhere: installing the latest version of Gatsby's modified reach-router. It's still unclear to me why this wasn't already installed as a dependency of Gatsby itself, but perhaps it was a version mismatch. Once this was installed, the site would actually load correctly on build, but it was still doing a full client re-render to recover from a different hydration error. This somewhat defeats the point of a static site generator, in my opinion.

The second issue was caused by an image component loading with the alt-text set on the server-side HTML, but empty in the client render. According to React, the alt prop for this component was empty, even though I could happily log out the property in console.

At this point, I gave up on trying to fix the hydration issue and instead just upgraded everything (again). This was partly inspired by the fact that Gatsby v5 has a mode called partial hydration, which basically allows you to turn hydration off for all components that don't need it. Imagine that, a static site generator that just generates static pages! Anyway, I still haven't tried this setting, but I'm keen to.

Step 4: Upgrades

It turns out that just upgrading everything more fixed my problems. Replacing the defunct gatsby-image plugin with gatsby-plugin-image (great naming convention) made the hydration errors disappear. It also allowed me to delete a bunch of image code hanging around from the original starter repo, so it was a win-win.

Feeling buoyed by my success, I upgraded Gatsby and its accompanying plugins to v5. Amazingly, this time nothing broke. So now the site is using a version of Gatsby that works in the Year of our Lord 2025. This should tide me over until the inevitable Astro rewrite 🙃.

Step 5: Bulma and styles

The last thing to upgrade was Bulma, the CSS framework I use. This was reasonably painless, and moved me away from using node-sass (bad, deprecated) to dart-sass (good, modern). Honestly I couldn't care less what compiles my CSS, but I just want things to keep working. The curse of JavaScript web development is that everything goes out of date immediately and has to be replaced with whatever is new, cool, and popular.

Anyway, this upgrade also allowed me to update my single scss file, the aptly-named all.scss, to remove a bunch of custom styles I no longer needed. The new version of Bulma is modular, and allows customisation of its various components by overriding Sass variables when you import them. Here's an example for the title element:

// all.scss
$site-accent: #004225cc;
$site-title-family: "Playfair Display", serif;

@use "bulma/sass/elements/title" with (
  $title-family: $site-title-family,
  $subtitle-family: $site-title-family,
  $subtitle-color: $site-accent,

);

This took me a while to figure out, but led to a very slimmed-down stylesheet, which I like. The only frustration is that some customisation options that seem like they should have corresponding Bulma variables just don't (looking at you, default font size). So unfortunately I couldn't delete my own styles entirely.

I took the opportunity to make the site look a little nicer and tidier. I'm no designer - check out Sim's site if you want to see what happens when someone who is a designer makes a personal website - but hopefully it's at least pretty readable and consistent.

Step 6: Actually write

Today we reached the final step of actually writing something, to salvage some meaning from my self-inflicted labours. I have to admit that while the first part of the journey was largely frustrating, I am happy with the end product, so I suppose I shouldn't begrudge myself going down the rabbit hole.

If you encounter any similar problems with your Gatsby site, I hope maybe you'll find a useful solution here. Otherwise, maybe you can take some inspiration to break things and then put them back together again better.