Portrait of Stuart Thomson

Stuart Thomson

Software Developer | Human Being

How this blog is built (pre-April 2022)
How this blog is built (pre-April 2022)

How this blog is built (pre-April 2022)

no-publish
This page has not been published.
ℹ️
As is tradition, the first post on this blog is a meta post about how I built it. This post is aimed at fellow programmers who may be interested in this type of thing.

General information

The stack

This website is built using React and Next.js, hosted on Vercel. Styles are collected and bundled using styled-components, though that may change. Running in the browser, there are web components using lit.
I wanted to build my site using React, as I didn’t want to be writing raw HTML every time I wanted to make changes. After using the library for many years, it feels like a natural choice for any form of web work.
I specifically chose to use Next, as I liked its model of being able to render pages on the server from dynamic data. Creating new blog posts, for instance, does not require a rebuild and redeployment of the site. I just write the post, publish it, and it is readable immediately.

No Javascript *

One major difference to most Next websites is that I’ve disabled its client-side runtime on all pages. By default, the statically rendered output includes references to all of the scripts and a copy of the props passed to the page to aid with hydration. It effectively means, for a website like this, the blog post content is included twice in the generated HTML.
This website is static. It does not need a bundle of scripts to run. It works just fine as static HTML. The client-side routing features wouldn’t add anything to this site besides loading more scripts and executing more code.
tsx
export const config = {
unstable_runtimeJS: false,
};
The asterisk in “No Javascript *” is because I use web components for some interactivity, which deserves its own section.

Web components

This section is less relevant to this blog (in which the only client-side Javascript is for analytics), but is used on the main site: sthom.kiwi.
The main issue with removing all of the Javascript the framework provided is that there is no defined entry point into whatever code I write. Usually you’d just write a new React component and be done, but this wouldn’t work. I can’t even write a new component that acts as a loader for more code, since it will not run on the client. I felt web components were a natural fit: the code is encapsulated to a well defined model, it’s fancy new technology, and it gives me an opportunity to learn something new.
I am using lit to define new components, which makes the process of creating a new component much faster. Create a class, add decorators, write code. It also provides some lifecycle methods, which are a really good addition to have when you components need to be set up and torn down.
I went down a bit of a rabbit hole trying to actually build and bundle the components. As these components are not included in the Next.js build, all dependencies need to be bundled together, and included in one single Javascript file. After much pain I settled on esbuild, which allowed me to add a single command in my build script before starting the Next.js build. One thing that surprised me was just how fast this built. I’m used to Webpack’s slow builds taking seconds or tens of seconds to run, but ESBuild bundled everything within 100 milliseconds (and that’s the first run, no cache). My code was compiled and bundled before I had even taken my hands away from the keyboard.
esbuild web-components/index.ts --target=es6 --bundle --minify --outfile=public/web-components/index.js public\web-components\index.js 595.7kb Done in 90ms
Including a component in a Typescript project was easy enough. Using declaration merging it was easy to specify the new element existed, and then I could just use it wherever I wanted to.
// lang:tsx twoslash //import React from 'react'; // ---cut--- // @filename: web-components.d.ts declare namespace JSX { interface IntrinsicElements { 'my-component': any; } } // @filename: index.tsx // ... <my-component /> // ...
A better type than any is certainly possible. If I find something better, I’ll make sure to post it somewhere.

The sthom.kiwi landing page

notion image
Aside from just having links, the home page sports a cheesy background effect: rotating shapes and code-like models.
The 3D rendering is done using Three.js, with the text models created in Blender using Fira Code as the typeface. This was shortly before I switched to using Jetbrains Mono as my main code font, and I haven’t updated those models yet. Whoops. The code for this background is defined in a web component that sets up the scene and renders the canvas.

Blog pages

My blog posts reside in Notion. This allows me to work on them independently from the code on the site. I’m using the nextjs-notion-starter-kit as a template, with a few customisations. One such example is the code block, which I’ve swapped out entirely to use syntax highlighting through Shiki, a library that produces HTML like the highlighting in VS Code.
Previous iterations have used MDX files in the same repository as the code generating the site, but I kept making the excuse that drafting posts would have to be done on a separate branch and the overhead of typing two commands was waaaay too much work, so I’d better not write anything at all.
At one point I had experimented with other external CMSs. I got reasonably far with Prismic, but decided against it in the end. The real nail in the coffin was when I started using Notion for my personal stuff, and everything just flowed from there.

The future

Now, on to the hard part: actually writing posts for this blog.
As for the code side, I do not know where this site will end up. This is the third time I’ve created a new blog, and each time they’re completely different under the hood. I’m happy that I’ve been able to patch together a bunch of technologies to create a site as static as I can while still having the option to include interactive elements. But, knowing me, I’ll find something that will justify rewriting eventually.
 
Comments (GitHub)