A much more pleasant way to write would be <Footnote>Like this.</Footnote>. And maybe do away with some of the HTML faffing.
And maybe write code like `<div>`, instead of <code><div></code>.
Luckily, this is all possible, with the power of MDX! MDX is markdown + JSX, which means we can write in markdown rather than HTML, and also write custom components like <Footnote> and drop them right in.
Of course, this opens up a whole can of worms. We're writing a website, so it needs to be HTML eventually, and our custom components need to be included, and if we want to write import then it becomes an "ESM module", which means it needs a "bundler", and---suddenly the project is ballooning out of proportion, all just because we didn't want to write <div> quite so many times.
The impression that I've gotten following this rabbit hole is that there are many complicated and annoying things that people want to do with websites, and as a result there are many complicated
and, some would say, annoying
"frameworks" to make things less annoying. But if you're just trying to write a static site, you don't need any of them!
In fact, it took me a while to get my bearings here. There was a version of this site that was a single page app with a bunch of React states and contexts managing everything, bundled with Vite. Eventually I found it was going to be a pain to make routing work the way I wanted, which was when I finally started to think that there must be a way to just make HTML files. Even then, it took some searching through existing tools
There's a Vite plugin vite-plugin-ssr that can do this! How do I set it up? Oh wait, it's now its own framework. Let's see, can I use it together with Vite, or do I have to migrate? Hmmm
before it occurred to me that I could do it myself.
If you want to start out with something else and end up with HTML, you need
a compiler.
That's it!
Static Site Generator From Scratch
Well, from esbuild.
And a bit of React.
Of course, you do have to find the right compiler for your setup. Since I want to write MDX with custom JSX components, a good tool for the job is esbuild with the @mdx-js/esbuild plugin. That whole part of the process looks like this:
This tells esbuild to go through all the MDX files in pages/, compile them (along with their imports, etc.), and put the result in out/.
Since we've eschewed all those fancy frameworks, we still have some more work to do---what this step spits out isn't a finished website. For each MDX page, we now have a Javascript file that produces that page's content. What remains is to convert it to HTML, wrap it in the trappings of an HTML webpage, and assemble them into a full site.
But the point is that this is easy, and doing it by hand gives us a lot of flexibility. To convert the compiled Javascript for pages/${slug}.mdx to HTML, we have a handy function provided by React:
import { renderToString } from 'react-dom/server'
const { default: Content } = await import(`./out/${slug}.js`)
html = renderToString(Content())
This produces HTML, but it still doesn't have the full page structure of <html>, <head>, <body>, etc. For our purposes, this boilerplate will be the same every time, so we can just write it as a string and insert the rendered html string in the appropriate place. Now that we have a fully-formed page, we can write it to our output directory (say, dist/).
And that's the whole process---just compile & render & layout each page, copy static assets (pictures, scripts, stylesheets) over to dist/, throw that all those steps together in build.js, and we've got our own static site generator!
to package.json. The first line warns esbuild that we're going to use big words like import, and the second tells npm what to do when we say npm run build.
Full setup on GitHub somewhere around this commit.
Well, that's not the whole process. Since we're writing it ourselves, we can do whatever else we want! Old HTML files from the previous version of this site? No need to rewrite them in MDX, just copy them through to dist/. Posts in a series with their own navigation? Just a quick JSX component. Whatever new features come to mind writing the next one? You bet!
Appendix: GitHub Pages
For some reason I find the documentation surrounding GitHub pages rather mystifying. In the end I've discovered that you can indeed tell it to "just build my shit and serve it", but it took a while to find the correct incantations.
For lack of anything more enlightening to say, I'll simply repeat them here. In .github/workflows/build-and-deploy.yml I have inscribed the following: