Stuart Thomson

Software Developer | Human Being

How this blog is built

How this blog is built

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 uses the nextjs-notion-starter-kit, with minor modifications to suit my tastes. The starter kit has had a lot of work put into it, and was a solid foundation to build upon.
The starter kit uses React and Next.js, and I’m hosting the site on Vercel. There was very little configuration for me to do.

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.
export const config = {
unstable_runtimeJS: false,
If I decide to add interactive elements in the future, I’ll continue investigating web components.


In a way, I don’t want my blog to become a hub of interaction or chasing metrics. As such, I’ve been hesitant to add comments to my site. However, I thought I’d give it a go. I’m using giscus, which embeds an <iframe> with comments pulled from GitHub discussions. I’ve linked it up to the repository, and we’ll see how it goes.
I’ve put the comments inside of an expandable area, so they’re not being shown by default. I feel this is a fairly good compromise, as only those who actually care will use them. For those who don’t want social aspect shoved into every website they visit, they’re hidden by default.


Content is written in Notion, which I also use for any major notetaking. It has blocks for basically all I need, and if there’s something else I want later, then I have ideas for how I can hack it together.

Syntax highlighting

The Next.js Notion starter kit uses Prism for syntax highlighting, but I, foolishly, wanted to include shiki-twoslash for interactive Typescript snippets. It basically mimics the behaviour in VS Code, including being able to view types and compiler error messages. I say it was a foolish decision, because it ended up taking a lot of time.
Shiki, the highlighting side of the module, is a compiled WASM module, which means I can’t do the highlighting easily in the Code component (since I have disabled runtime Javascript). This means I need to pre-process the code to an HTML string when getting the page’s props, and then pass that through to the component. It’s fairly ugly, and uses raw HTML, but at least it should be sanitised through the pre-processing.
Twoslash, the Typescript hovers side of the module, was fairly easy to implement. The main problem was in determining whether to use twoslash or not, since most code blocks won’t need it. I ended up adding a magic comment to the first line of code blocks that allows me to override the language (in case Notion doesn’t support the language I want to use) and also include options (similar to how the remark plugin works).

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)