Process of migration from WordPress to Astro

I want to be upfront and honest about how I use AI. I won’t get into the details of my stance on AI, but in summary, I dislike its impact on the environment, I see its value in doing analysis or repetitive tasks, and I abhor its use and current impact on arts, music, and creative fields. I believe we should be always be thinking about how we use it and how that can change over time. I use AI regularly for my occupation as a software developer. I use it very little, if at all, for things outside of work.

For this project, I used AI to create scripts and grep/sed commands that I could run manually (I didn’t connect AI to read my code) to ensure my files were compatible with my planned Astro setup. I also asked AI via chat to convert certain strings of PHP/WordPress hooks to a JavaScript version. This sped up the process for me, given that the end goal was really to have a website that I can blog on, with a process that’s faster. I did not want to spend too much time working on the technical parts because that was the less important part of this work.

Why Astro?

I mentioned in my previous post that I made a decision to use Astro after gaining familiarity from my job. One of my coworkers mentioned it quite a lot and then a few of us jumped on board giving it a go for some documentation. When it came to my blog, I honestly didn’t bother doing any more research for myself beyond this, which goes to show how much I really didn’t care about the specific static site generator I used. But a few people saying good things, explaining a bit about how it was easy to build a layout (JS and Markdown nicely together), and seeing some similarities with React, I decided it was worth a shot.

Conversion process

Converting posts and pages from WordPress to Markdown

You’d think this part was easy—so did I. Firstly, I exported all my posts and pages through the WordPress admin interface, then used a WordPress Export to Markdown tool. I thought about having the comments display on my old posts even though I wasn’t going to have a comment system, so I tried a fork of the repository that exported them too, just to see what it would look like. Of course, I didn’t end up using them, for reasons I’ve already shared before. The comments are often part of a conversation between me and friends, the other side of which is lost to time due to my friends’ blogs no longer existing. They are often also irrelevant to the topic of the blog post. So, with a tearful eye, I bid them farewell.

Initially, I forgot to read the tool’s readme thoroughly, which resulted in a lot of missing info the first bunch of times I ran the script. (Classic… 😅) I hadn’t specified all the custom fields I wanted to convert. When I figured that out, it was a lot less complicated than I realised. YMMV, obviously, but this was what I used:

--post-folders=true --prefix-date=false --date-folders=none --save-images=none --output=blog-posts --frontmatter-fields=title,date,categories,coverImage,excerpt timezone=utc+11 --include-time=true

I already planned to do individual post folders because it mimics the structure of my URLs, which is /post-name. I had used unique URLs since day one of using WordPress, so during this process, none of the folders would end up being the same name.

Location of images and media, and my manual process

I really disliked the way WordPress did images with the media uploader and all of that nonsense. I had my own custom and kind of manual way of doing things. After a number of years using the media uploader (perhaps in like 2022) I did away with it and actually made a new images folder in the root of my website, then manually organised folders by year and then by post name (e.g. /images/2026/astro/). I would also upload these manually. It didn’t bother me, because I preferred the structure and it matched how I kept the files on my computer. It does mean that I don’t have a lot of smaller versions of images, but I more or less said, “bugger off WordPress, suck it up”. I mean, of course I’m potentially sacrificing performance and forcing people to load larger images sometimes. 😅 But I made that call, and I admit it’s not the best. I want to say that I don’t have an image-heavy blog—funny because the ones with lots of images are the travel posts—but I’m also on a continuous mission to never put a ridiculous amount of photos in those posts.

Since I had my images already nicely organised, I also had the images in my posts pointing at a relative URL like /images/2026/astro/cool-bananas.jpg. I had some custom WordPress shortcodes for displaying images, and they were optimised to use the figure and figcaption elements in HTML. My plan was to convert these to the correct format for the conversion, and the relative URLs could stay the same since they still reference the images folder.

For older posts that reference the ol’ /wp-content/ folder, I kept those references the same. I removed the WordPress content post-conversion, uploaded my whole blog’s static files, and I kept that folder.

Writing my WordPress theme in Astro

As a weird celebration, this look and feel of my blog has been the same since 2018! I find that difficult to believe. I come from the community of bloggers who changed their layouts every couple of weeks to a month. Because I was very online, and coding was my hobby, I sometimes felt at liberty to change my layout after a week because I had the time.

Grep commands and sed commands for more custom work

This was the point at which I got stuck and felt like giving up. It was one of those hurdles that made me regret starting this project in the first place. I thought I’d made it too complicated and had gotten myself into this hole. But these are the moments we need to push a bit harder, because we often find more motivation on the other side of the hurdle. It was at this point that I felt more open to using AI to help me write some of the commands I needed, because the thought of doing it on my own was really doing my head in. Here are some examples:

  • In all index.mdx files in all subfolders in a given location, I need to insert a line as the first line of frontmatter, before anything else
  • In all index.mdx files, if there is a certain string in the post body that starts with <Figure, I need to add an import line on a new line immediately after the end of the frontmatter
  • Convert the following PHP into JS and ensure the date is ISO: <?php echo get_the_date('Y-m-d'); ?>T<?php echo get_the_date('H:iP'); ?>
  • Find all instances of (Y) and replace with 👍🏼 (yeah really—there were also some others)

The other reason for doing this was to avoid having to do a mass find-and-replace, which is kind of gross in a code editor. After going through this process of getting things working in Astro the same (or in at least a similar) way as they were in my WordPress installation, and making sure my .mdx files had almost everything I needed, it really unblocked me for the rest of the project.

Refactoring and improving the CSS

Because I am a CSS guru, I am inclined to care about improving this. 😅 I had used CSS custom props throughout my code, which was an improvement I made a while ago, but I used a parent class .dark to style the entire dark mode of my blog. light-dark() or prefers-color-scheme: dark were features of CSS that I learned to use after I implemented dark mode, and I wanted to use them to tidy up my CSS. It would also mean I could display a theme/mode depending on the user’s system setting.

The CSS custom properties are currently still a bit of a mess, but I have mostly been consolidating them to all use light-dark(). For example, for links, I currently use the custom property --link: light-dark(var(--primary), #5bccf1);. To set the correct theme, I base it off an ID that is on the dark mode toggle in the top right corner of my website:

@media (prefers-color-scheme: dark) {
    :root:has(#theme-switch:checked) {
        --color-scheme: dark;
    }

    :root:has(#theme-switch:not:checked) {
        --color-scheme: light;
    }
}

Decisions I made to make life easier

Apart from deciding to build or do certain features later—such as the search functionality and the category pages—this is just a small list of decisions I made while I was working on this conversion that made my life easier:

  • Don’t display a thumbnail image for every post in existence. Go for a handful on the homepage, then link to a page of archives. My reasoning was that people would hardly be going through pages of previous posts in a visual way, and the homepage would be the eye-grabbing most recent posts.