Rebuilding this site in Astro (with headless WordPress)

For a while now, I’ve been bothered by keeping an entire WordPress installation exposed to the internet for a blog I only update once or twice a year. Additionally, the flat file CMS I used to generate the rest of elizabethpowell.net was now deprecated and didn’t play nicely with supported versions of PHP and Twig. So a new solution was required.

I knew I wanted to use Astro, as it seemed like a great blend of static site generator and framework that could turn multiple content sources into a single, seamless site. I spent some time exploring various modern headless CMSes, but none of them quite beat WordPress for ease of writing experience. (A full review of headless CMSes may eventually be the subject of a separate blog post.)

I owe a lot to the series of posts starting with Building an Astro Website with WordPress as a Headless CMS on Full Stack Digital for getting me started, along with the Astro documentation. I ended up refactoring my code from the tutorial to use the Astro Content Collections API, both to access my blog contents and my recipes, which are stored as markdown a la RecipeMD.

Blog

I started by setting up a new DDEV container for WordPress, then added a new nginx configuration to move the WordPress instance to a subdomain. I then set up Astro following the configuration for DDEV’s own Astro repository for its documentation site.

DDEV still tried to copy its configuration files to my docroot every time I restart so I disabled the automatic configuration setting in .ddev/config.yaml.

To help wp-cli find my WordPress installation I added wp-cli.yml at the project root with contents path: wp/

Image Management

The biggest hurdle was what to do with images inserted inside the body copy with WordPress. WordPress itself adds the files as media items, which are straightforward to retrieve, but the references inside the content field are part of the HTML content block (at least for the TinyMCE editor I use). I tried several methods to import them into Astro “properly” so that Astro would optimise them, such as:

Eventually I realised that since WordPress was already generating thumbnails for me, I may as well not do that twice (i.e. once with WordPress, once with Astro). So I set up a symlink from my public directory to the WordPress uploads directory, and use cheerio to parse the content and update the src target.

In .ddev/config.yaml:

hooks:
    post-start:
        - exec: exec ln -srfn wp/wp-content/uploads site/public/assets/images/blog

I also added a script to package.json to force a restart of Astro dev and thus reload all the content:

"scripts": {
    "restart": "supervisorctl restart 'webextradaemons:*'"
}

Recipes

My recipes are written in Markdown to keep it simple and easy to maintain. The format is influenced by RecipeMD but doesn’t strictly adhere to it since I have some custom scripts and I like to provide multiple units per ingredient (e.g. volume AND weight).

The biggest problem was that I needed to be able to parse and modify the markdown data before it was loaded into the Collection, which didn’t seem to be possible with the default Glob parser. This also meant every time I changed content I had to do a full ddev restart before I could test it with my theme, which took minutes. I ended up implementing the Glob With Parser function from jdunning on Stack Overflow.

Watercolour Pigment Database

Thankfully all this required was a bit of styling. It’s a .md file with inline scripts and it all worked smoothly once my custom JS file was in the assets directory.