I Rebuilt This Site for Three Years
May 16, 2026

I Rebuilt This Site for Three Years

1098 Words|6 Minutes to read

I rebuilt this site for three years.

It was supposed to be a weekend job. The old version ran on Gatsby — fine, fast enough, but I'd been hitting walls. The routing and GraphQL pipeline were rigid in a way that fought me every time I wanted to add anything new. There was no built-in image optimisation. Every new dependency landed with its own dev-server slowdown. Meanwhile, every other project I touched was on Next.js, and the contrast got worse every month.

Next eventually shipped file-based routing of its own. Then next/image, next/font, next/script, the metadata API, next/og, the package-import optimiser. None of it world-changing on its own; together it stopped being a framework and started being a runtime with batteries. The first Saturday of the rewrite ported routing. The second Saturday threw out the data layer. The third Saturday ate the design.

Three years later I'm writing this post on the version that finally shipped.

81 commits, 302 files changed, +18,184 / −17,787 — the PR that finally landed.
81 commits, 302 files changed, +18,184 / −17,787 — the PR that finally landed.

The App Router changed it twice

Next 13 was halfway through the rewrite when it dropped the App Router and React Server Components. The whole model of "fetch the right thing at the right rendering tier" got simpler. The old per-page getStaticProps/getServerSideProps ceremony collapsed into just await the data in the component. SSG, ISR, dynamic — picked per component rather than per route. The leaf decides. The page stays static when none of its leaves are dynamic. That's how this site's home page is statically prerendered while the Spotify and GitHub feeds quietly fetch themselves from edge-cached route handlers.

I ended up rewriting the rewrite. Worth it.

The Spotify thing has been on my list for five years

The integration on this site — the spinning vinyl, the per-track metadata, the artist play buttons, the persistent player that survives navigation — is the latest version of an idea I've had since I shipped a "now playing" widget on my GitHub README years ago. The widget did exactly one thing. Every other variation I tried since then died in scope creep.

This one lives because the rewrite finally had the scaffolding to support it: a refresh-token flow that lives server-side, route handlers that edge-cache, a client provider that mounts at the layout root so playback survives client-side navigation. The pieces existed. They just needed somewhere to plug in.

Tailwind, eventually

The Gatsby site was MUI. The rewrite migrated incrementally — I kept the MUI icon package around for a long while, importing one or two glyphs at a time as I ported each section. By the time I deleted the last MUI import the design had quietly become a tailwind+css-variables system with its own pill-button vocabulary, a global Surface and Paper split, accent tokens that flip per theme. None of it dramatic on any single commit. The whole arc, in hindsight, is the most opinionated thing about the codebase.

Turbopack made dev tolerable

The really bad part of Next for a while was the dev server. Cold starts that wouldn't finish in a coffee refill. HMR that paused for thoughts. Turbopack going stable changed the calculus — startup is instant, edits propagate immediately, and the loop that had been making the rewrite feel slow stopped being slow.

A template fell out of the rewrite

Most of the time I spent on the migration wasn't moving content. It was building the chassis I wanted to live inside. A markdown content layer with auto-detected galleries, image optimisation that doesn't require you to think about it, frontmatter schemas with cleanly typed loaders, a build-time image sync, a watcher, a CLI scaffold. The same chassis I started porting into a separate project: edgetatic. The Gatsby version of this exact site became a reusable template there — "The Great Gatsby" — built from the same content layer I was hardening here. The lineage runs both ways now; whatever the markdown layer learns on this site ports back to edgetatic as a generic primitive.

The starfield

The animated starfield in the sidebar is a small thing nobody would notice unless I pointed at it. It came from a different project entirely: 301st.xyz has a small game with a particle system called PyroStars.

PyroStars game from 301st.xyz
PyroStars game from 301st.xyz

The site's sidebar starfield is the same trick, dialed down to ambient. A canvas, a couple thousand points, parallax against scroll, a ripple on click. The "this small effect ate the whole evening" kind of thing. Months later it became the visual signature of the homepage.

What it cost

Most of what I just listed didn't need to exist. The Gatsby site would have served me. What I rebuilt was not the site — it was the shape of the codebase I wanted to live inside. A new section is now five minutes. A new content type is a frontmatter field plus a render. A new third-party integration caches itself sensibly and degrades silently when its credentials are missing. None of that ships visibly to a reader. All of it was the point.

The visible cost is real. People don't see the version you don't push; they see the version you don't update. For three years the deployed site barely moved while I rearranged its skeleton in private. If you're a reader of this blog, I owe you posts. If you're someone considering a rewrite of your own, I owe you this:

  • Merge sooner than you think you should. Six months of polishing rarely beats shipping the messy version and iterating in public.
  • Treat the migration as a deadline, not a habit. Mine became the latter. I'm not unhappy with the result, but I lost three years of writing here.
  • Give yourself a small reason to finish. The thing that finally pulled this in was adding a tiny links page. Not the point of any of the work — just the smallest finishable surface on the pile. The moment the rewrite was scoped around shipping that, the rewrite shipped.

The version you're reading this on is the one I finally pushed. It is not the version I intended in 2023. It is better than that one, because three years of trying things in private — even the things that didn't pay off — left a codebase I can move quickly in now.

Posts to follow. I have a lot of them.

  • meta
  • nextjs
  • gatsby
  • rsc
  • tailwind
  • rewrite

Comments