Dark Mode

Nick Van Exan

Software Developer

Astro + Keystatic + Markdoc

Migrating to Astro + Keystatic

Hello from Astro!

Last week, I re-wrote my website, porting it from a custom Markdoc build solution to a new solution that leverages Astro and Keystatic. This is a short summary of my journey.

Motivation

A couple of years ago I ripped out Next.JS and MDX from my personal website, unhappy with the fact that my users had to download React and related framework JS for what was a very simple and primarily text-based website. I replaced it with Markdoc and a couple of small build and compile scripts. You can read more about that journey, and the performance gains I got from doing that.

Everything so far has worked wonderfully. I write my posts in markdown either in iA Writer or directly on GitHub . The changes I make against the GitHub repo automatically trigger a new Vercel build and deployment. The build and deployment takes < 1 minute. Just commit a new Markdoc file to the repo and you have a new blog post. Simple and effective.

What I loved about this solution was that I wrote the build scripts, compiler, templates, etc. myself and did not have to rely on hardly any libraries / frameworks for implementation. I am a minimalist. My site was about as minimal as it gets. There was no third party libraries to maintain other than Markdoc, and honestly I could never have updated that library and it would have been fine. This made my code very resilient to changes. And to me that's a huge win. The less tooling I use, the less there is to maintain, the more the solution will last amidst all the new hotness that may surface in web dev land one day and be gone the next.

But there's been a few pain points:

I wanted to solve these pain points, but with the same hard requirements I had two years ago:

I was happy to discover that others with a great deal more money and time than me have also been working on leveraging Markdoc to deliver blazing fast content websites with minimal JavaScript. Enter Astro + Keystatic.

Astro

Astro is self-described as a framework but it's unique among other frameworks in that it ultimately produces HTML and CSS and only JS when / as necessary. It achieves this through an island-based architecture. In a way, it's a much grander extension of my initial idea. It has support for routing, can do hybrid apps that are statically generated as well as server side rendered and client hydrated, and has wonderful opinions about integrations with web-components. It's simple to get the hang of and has a really ergonomic approach to routing and templating.

Keystatic

Keystatic is a file-based CMS by Thinkmill that leverages Markdoc to allow you to live edit content on GitHub or your local file system in a very cool way. With Keystatic, you can spin up a fairly robust CMS that gives you a great interface and workflow for creating and editing and Markdoc-based content. It's awesome. And makes content management with markdoc super easy.

Here's a quick example of what it looks like editing this very post in Keystatic:

Writing a blog post in Keystatic
Writing a blog post in Keystatic

Bringing It All Together

Migrating my existing Markdoc site to a solution based on Astro and Keystatic was pretty straightforward. I created a new repo based on the Keystatic Astro quick start. I then set up my Keystatic schemas and copied over my existing Markdoc files. This was mostly just a matter of copy-paste, with a couple of tweaks to some frontmatter fields. Resilient technologies ftw!

The one thing that Astro + Keystatic's Markdoc implementations lacked, however, were support for parsing footnotes and highlights. The Markdoc team does not favour augmenting their parser to parse these items, preferring instead for users of Markdoc to write their own custom tags.1

I am not in favour of this approach for three reasons: (1) I find Markdoc tag syntax less ergonomic than basic markdown syntax; (2) the Keystatic approach to dealing with custom tags inside of a rich content editor in the CMS, via its componentBlocks API, is a bit too rudimentary for my liking at this point; and (3) the way iA Writer renders markdown in its app is how I also render markdown on my site, and so if I open a Markdoc file in iA Writer for content authoring or editing, I want to see the same renderings for highlights and footnotes.

Here's an example of how iA Writer interprets and renders highlights and footnotes...

Editing a post in iA Writer
Editing a post in iA Writer

I succeeded in getting my desired result without custom Markdoc tags by taking the custom parsing logic I wrote for my existing site and publishing it as an NPM package (@nvanexan/markdoc-extensions). This package just has a simple parser that extends the existing Markdoc parser with optional support to also parse highlights and footnotes. I simply import this package into my new site's repo, and then patch-package the @astrojs/markdoc library, substituting my extended parser for the vanilla Markdoc parser. Works like a charm.

Screenshot showing highlights and footnotes on my site
Screenshot showing highlights and footnotes on my site

Results and Performance

I am very happy with the results of this experiment. I now have a nice CMS-like editing experience when I want it, with the flexibility of still being able to edit and author content in my repo directly even without Keystatic, because it's just Markdoc at the end of the day. Resilience!

What about performance? I am thrilled to see that the lighthouse scores I achieved two years ago when I ripped out Next.JS and MDX and replaced with my own Markdoc solution are still maintained. Here's a comparison of all three versions of this site (Next.JS, Markdoc custom, Astro + Markdoc).

2019 site - using Next.JS and MDX
2019 site - using Next.JS and MDX

2022 site score - custom Markdoc solution
2022 site score - custom Markdoc solution

2024 site score - with Astro + Keystatic + Markdoc all working together
2024 site score - with Astro + Keystatic + Markdoc all working together

Concluding Thoughts

Porting my site to Astro was really easy, because I had started with a resilient Markdoc-based solution. I am a bit uncomfortable about the maintainability of an Astro-based solution (thinking of my experience with GatsbyJS for example of how promising framework ideas can go sideways pretty quickly) but it was a perfect quick win to give me some CMS-like capabilities without having to sacrifice performance. I think I can summarize my thoughts on this tech as follows...

  1. See issue 70 and my pull request 40 in the Markdoc repo for more details.

Nice, you made it to the end of this post. Thanks for reading. Other posts by me are over on my writing page. If you'd like to subscribe to this blog, you can do so via RSS or Email Newsletter. If you found this post helpful, and want to support more of this content, you can also buy me a coffee.