Daryl Wright /     (6 mins)

Summary

  • Converted website from Gatsby to Next.js
  • Improved theme support without flashing on route changes
  • Created a new Rehype plugin to display GitHub Gists

Details

Migration from Gatsby

The Gatsby upgrade to v5 from v4 didn't go so well, so now we're here at v3 of this website. This was the final straw of problems I had been experiencing with Gatsby that led me to finally transition away from it.

This article by Seth Corker was a major inspiration for me to make the switch. Some of the issues that Seth discusses in his post are those I've experienced myself, such as the frustratingly slow builds and unpleasant upgrades.

One issue I had with Gatsby that isn't mentioned in Seth's article is its poorly documented API. It's not obvious how to use the various APIs for gatsby-node.ts, gatsby-ssr.tsx, and gatsby-browser.tsx. It often takes a bit of research to figure out how it all works due to the incompleteness of the documentation.

Additionally, the API is weakly typed and therefore a bit annoying to use with TypeScript. All this leads to unnecessarily complex code that is difficult to maintain, especially across major versions.

Another issue I have with Gatsby not mentioned in the article is its codebase. Its architecture is not very intuitive and the source code is difficult to navigate. The complexity of the codebase makes it hard for Gatsby to smoothly transition to ECMAScript Modules.

ESM is required when using modern Rehype and Remark plugins to preprocess MDX content. Unfortunately, plugin setup is done alongside site metadata definitions in the gatsby-config.mjs files, which depend on utilities shared across the site.

While it isn't difficult to import CommonJS in ESM, this gets complicated when using TypeScript, something the Gatsby team has not been able to support as of this writing. For the utilities that I need in gatsby-config.mjs, they needed to be duplicated so that there is one version supporting CommonJS and another supporting ESM. I was not happy with this solution, and it soured my upgrade experience with Gatsby.

All of the above, including the long build times, made continuing to develop this site on Gatsby unsustainable. I'd like to actually spend time creating new solutions, rather than maintain this site. So, I decided to invest the time now to convert over to Next.js, with the hope of freeing more development time in the future.

Migration to Next.js

Frustrated with Gatsby's upgrade experience, I decided to create a prototype of this website with Next.js. I started with a fresh Next.js app using create-next-app and imported code from the Gatsby site bit by bit.

Without getting into too much detail, it was fairly straightforward to convert everything over. The Next.js API is wonderfully documented, which helped a lot with issues transitioning from one paradigm to the next. I've even found it easier to work with site data without GraphQL, just plain JSON or JavaScript objects 🤯.

The ESM experience is what truly had me sold on Next.js. Unlike with Gatsby, I didn't have to fight with it. I could use an ESM config file (next.config.mjs) to take advantage of Rehype and Remark plugins, while keeping the rest of my code off ESM (at least until there is more seamless TypeScript support).

I mentioned that I created a prototype of the website from a fresh Next.js app. This created a problem for me as the prototype began to resemble the actual product, and I wanted to keep the commit history intact. If the prototype was a branch, this would be a trivial task, but unfortunately this was not the case.

I wasn't going to let that stop me though. With a little bit of research, I was able to "stitch" an unrelated history into the original website repo. Note that a change of this nature is usually breaking, which is why this website is now sitting at v3. I'll detail the steps I took below, but this will be messy, and should not be done under normal circumstances. Proceed with caution if you choose to do this in your own project.

# Navigate to the branch you would like to merge into and run...
git remote add local /path/to/unrelated/repo/
git fetch local

# Create a branch to perform the merge to be on the safe side
git checkout -b feature/risky_merge

# Merge the unrelated tree
git merge --allow-unrelated-histories local/main

# Amend the commit message, if necessary
git commit --amend -m "feat: merged feature/risky_merge into dev" \
-m "Converted to completely different framework." \
-m "BREAKING CHANGE: Changed frameworks"

# Remove the 'local' remote
git remote remove local

In my case, because the prototype was going to replace the old site, I committed the deletion of almost all the files in the original repo before merging. This ensured I would complete the merge without having to resolve endless conflicts. If the original and unrelated tree are tightly related, it might be better to just merge normally and resolve conflicts the best you can.

Improved theming support

I detailed this solution here as an update.

A new Rehype plugin, rehype-gist

I previously rendered GitHub Gists using the gatsby-node API in Gatsby. This was after I was not able to get the plugin gatsby-remark-embed-gist working. I took source code from that plugin and refactored so that it would work in gatsby-node.

However, when transitioning over to Next.js, I had to reimplement the Gist code in that environment. I ran into several issues trying to create a separate component for this task, particularly around fetching the Gist data from GitHub. I finally settled on creating my own internal plugin, which then became a separate project. Thus, rehype-gist was born.

With rehype-gist I no longer had to create a separate React component to display Gists. I could 'request' them via a specially formatted URI in an inline code tag and let the plugin handle the rest. View the source and install via NPM.

Updated July 11, 2023