Moving this site to Astro

I started on a redesign of this site back in May 2020 after deciding I wanted a simpler, content-first statically generated site.My main goals were to keep the core as simple as possible – writing in markdown and minimal boilerplate code – while allowing me to noodle around with new-fangled CSS & web-native stuff when I want to.

After about three years running on Jekyll I started to get frustrated at some of the stuff I couldn’t do easily - primarily the difficulty of working with custom components in markdown. I was using some kinda-janky markdown pre-parsers to let me insert things like callouts…

{:.callout}
This is a callout

Which wasn’t exactly easy to work with and more importantly, if I ever decided to switch away from jekyll and export all my markdown files, isn’t exactly obvious or intuitive.

What I really wanted was the ability to use HTML- or React-like components in my markdown. And as with web components or react components, I wanted to define these as self-contained components with scoped CSS and the like.

So in Jan 2023 I started to play with AstroJS , which had hit 1.0 six months earlier and was the latest cool kid on the block when it came to static site generators.

By far the most appealing thing about astro in early 2023 was its somewhat native support for MDX , its React-like component architecture where .astro files contain scoped HTML, CSS and JS and the relatively small amount of boilerplate code necessary to get a basic statically-generated blog up-and-running.

So I quietly ported this site over to Astro in Feb/March 2023 with the intention of cleaning everything up a bit later, but with the release of Astro 3.0 in August ‘23 so much had changed that it made more sense to start again from scratch. So I quietly switched to the current codebase around the time Astro 4.0 was released in December ‘23.

Why Astro?

Modern, Simple & Content-First

Astro is very well designed, and because it’s built with a modern typescript-based stack, it comes with all the advantages you’d expect (typesafe javascript among them).

When used as an SSG, Astro is also very simple. File-based routing, markdown parsing and YAML frontmatter Just Work™ and stuff like image optimisation come out-of-the-box. And while composing pages from astro components feels very Reactish, it’s “zero Javascript by default” – the generated site will only include client-side JS when necessary, which for this site is hardly ever.

Astro has an extremely strong ecosystem of plugins, tools and themes around it.

Content Collections

Content Collections let us define sets of content and their properties via zod schemas, and then load the relevant content from a collection of Markdown or MDX files. I currently have two content collections: Articles and Notes.

  1. Articles - Longish-form articles (see /writing )

    • title - The article title
    • slug - URL slug (optional, generated from filename if not provided)
    • draft - Whether the article is published or still a draft
    • description - Meta description for SEO and article cards
    • pubDate - Publication date
    • updatedDate - Last updated date (optional)
    • cover - Cover image (optional)
    • coverAlt - Alt text for cover image
    • tags - Array of topic tags
    • platform - For articles published elsewhere (Medium, etc.)
    • redirectURL - URL to redirect to if published on another platform
    • styleguide - Flag for articles used in the site styleguide
  2. Notes - Short thoughts or comments on other people’s stuff (see /notes )

    • title - The note title
    • sourceURL - Link to the thing being commented on (optional)
    • slug - URL slug (optional, generated from filename if not provided)
    • draft - Whether the note is published or still a draft
    • description - Meta description for SEO
    • pubDate - Publication date
    • tags - Array of topic tags
    • styleguide - Flag for notes used in the site styleguide

Astro Components & MDX

Components can be anything from a tiny snippet of HTML to a reusable UI component (which takes props and/or content) to a whole page layout. Here’s an example.astro component:

---
const { title } = Astro.props;
const theNumber = Math.random();
---
<section class="mything">
<h1>{title}</h1>
<p>Number: {theNumber}</p>
</section>
<style>
h1 {
color: red;
}
</style>
<script is:inline>
alert('Hello');
</script>
  1. The TypeScript in the component script at the top runs only at build time. The ability to co-locate this “server-side” code with the component is super useful for keeping things organised.
  2. The component template (below the last ---) is the HTML which will be rendered when the component is used, except (much like JSX) you can use variables defined in the component script , or other astro components imported in it.
  3. The CSS in the style tag is scoped to this astro component only.
  4. The script tag at the bottom uses a special astro directive, and will be executed clien-side.

Building a site using Astro components is much the same as using react components (or partials in other SSGs). But these components really come into their own when used inside Markdown Content.

MDX Components

MDX lets you use JSX inside markdown file, and is one of the main reasons I chose Astro over other SSGs. In Astro, you can import .astro components into mdx files and use them in the same way you can anywhere else. I’ve ended up with quite a lot of really simple components which just render some HTML and apply some scoped CSS to it. Here’s my highlight.astro component:

<span class="highlight"><slot /></span>
<style>
.highlight {
background: var(--color-highlight-bg);
padding: 0 8px 4px;
border-radius: 4px;
}
</style>

And now when I’m writing in markdown I can use it like this:

Here is some text <highlight>that is highlighted</highlight>.

This really comes into its own with more complex components like callouts…

<Callout emoji="💡" type="blue">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Earum hic a dolorem ipsa labore cum.
</Callout>

…which renders…

The combination of Astro’s component-based architecture and components-in-MDX-files ticks some super important boxes for me:

  • Components make for easy maintenance long term. Especially when dealing with CSS.
  • Scoped CSS and JavaScript allow me to CSS without worrying affecting parts of the site.
  • Using components in Markdown makes it extremely easy and quick to write fully featured content.
  • If I ever want to switch away from Astro, the structure of Astro components and how they’re used in Markdown files should make it fairly easy to port my content to another system.

So Where am I now?

Two years into running this site on Astro, I’ve settled into a pretty straightforward setup that gives me everything I need without much fuss.

Content Collections

The site uses two main collections: Articles and Notes. Articles are long-form pieces like this one – they live in /writing/ and support cover images, tags, and a bunch of other metadata. Notes are shorter, usually links to interesting things I’ve found with a bit of commentary. They’re deliberately minimal: just a title, optional source URL, and publication date. Both use the same filename pattern (YYYY-MM-DD-slug.mdx) which keeps things organised and makes it dead simple to create new content.

Plugins

I’m using a handful of plugins that handle the boring stuff so I don’t have to think about it:

  • Expressive Code handles syntax highlighting for code blocks.
  • Astro Icon lets me use SVG icons without manually importing and managing them. I can just drop <Icon name="heroicons:home" /> into any component.
  • Sitemap generates a sitemap.xml automatically, which is one of those things that’s trivial but annoying to maintain by hand.

On the markdown processing side, I’m using a few rehype plugins:

  • rehype-autolink-headings adds anchor links to headings (so you can link to specific sections)
  • rehype-external-links automatically opens external links in new tabs
  • rehype-mermaid renders mermaid diagrams at build time.

I’ve also got a custom remark plugin that calculates reading time for articles.

Components

One of the big selling points of Astro is the ability to use components directly in MDX files. I’ve got about fifteen components in src/components/mdx/ – things like <Callout> for highlighted notes, <Accordion> for collapsible sections, <BookmarkCard> for pretty link cards, and <Notion> for embedding Notion links with a custom style.

These are just regular Astro components with scoped CSS, which means I can iterate on their design without worrying about breaking other parts of the site. And because they’re used in MDX, the markdown stays fairly readable even if I decide to move away from Astro in the future.

The page layouts themselves are pretty straightforward – components like BaseHead, MainNavigation, and Footer get composed into layout templates like Article.astro and Note.astro, which wrap the markdown content. The whole design system is based on CSS custom properties defined in global.css, so switching between light and dark themes (or tweaking colours) is just a matter of changing a few variable values.

Future Plans

There are some notes & ideas on GitHub , but I’m not in a particular rush to ship any of it. I’d rather spend my time working on Astro Editor or using it to write stuff here.

copy / view as markdown