Migrate WordPress to Eleventy with Colocated Images
I have a few old self-hosted WordPress sites that need to be migrated off a VPS. I’m still happy with the content and don’t want to just dump them, but I also don’t want to have to keep managing a WordPress installation for what are now static sites.
The core requirements for my migration are:
- Markdown support
No more fussing with a database or headless CMS. I need flatfile. - URLs to match WordPress
Some of my posts seem to get hits still and perform quite well, I don’t want to mess with the SEO or anyone’s bookmarks. - Colocated images
One thing that drives me batty about a lot of these frameworks is the poor support for colocated images with posts. Why is this such a massive issue? - Off the shelf themes, preferably TailwindCSS because that’s what I’m using predominantly at the moment, but I’ll take anything nicely structured and easy to manage and tweak.
I’ve been using Next.js for most of my other projects, but those have more complexity, this is just a blog. NextJS feels like overkill.
Zola seemed like a good idea at first glance, but they don’t support permalink customisations, and the structure is quite restrictive. The themes available are also thin on the ground and mostly unsuitable for what I wanted.
Searches suggested Eleventy, although there are several complaints about the challenges of colocation of images, usability of documentation, amongst other things. I have taken a look at it before as a framework for my other sites, but as others noted, the documentation isn’t as approachable as with other frameworks.
That said, a starter is worth a shot and there is this helpful guide by Scott Dawson for migrating WordPress to Eleventy and deploying to Netlify, which is exactly what I intended to do.
And thus, we press on. Or unWordPress on.
Step 1. Export WordPress
Use the built-in WordPress export tool to dump the entire site to xml.
Step 2. Convert WordPress export to markdown
Thank goodness for tools like wordpress-export-to-markdown
.
Does exactly what it says on the tin. I used the options to create year, month, and individual post directories, save the attachments and scrape for external images.
Step 3. Set up Eleventy
The guide by Scott Dawson recommended a different starter project, but I found vredeburg which incorporates TailwindCSS and looks almost the way I want for a simple blog.
Clone it.
The theme works as is out of the box, but you can’t quite just drop in your converted posts. Will get to that shortly.
Still, you can move the converted posts in now. Copy the converted output/posts/
into src/posts/
. The conversion also puts pages in date folders but they don’t need to be in that structure in Eleventy, so copy the leaf folders in output/pages/
into src/
.
Site configuration
- Update
_site/author.json
and_site/site.js
with your own details. - Update
menu.json
with your intended navigation items. - Update
tailwind.config.js
to suit your tailwind theming preferences. - Update the images in
src/assets/img
.
Easy enough.
Step 4. Image colocation support
This is the where the bulk of the work needs to occur.
- Install the following packages:
Loading...npm i -D markdown-it-modify-token
- Update
eleventy.config.js
with (comments inline):
This allows you to colocate your images and makes pointing them to the right place in dist
build’s problem and not yours.
You can keep using ![]()
syntax and it will ‘just work’.
In the post-grid.njk
partial or wherever you put your post loop, you can also modify the cover image tag to use the colocatedImagePath
filter:
Loading...<img src="{{ post.data.coverImage | colocatedImagePath(post.filePathStem) }}" alt="{{ post.data.title }}" />
Step 5. Update markdown files
There are a few changes you will need to make to your markdown, sorry.
-
If you use WordPress galleries, you will have to manually convert the shortcodes to markdown image syntax and map the asset IDs to the colocated files.
-
If WordPress uses a resized image instead of the original, you will need to remove the
-widthxheight
portion of the filename. You can use a regex here. -
You will need to search for any other shortcodes to replace them with something else.
-
None of my
code
orpre
content seems to have been converted into markdown. Those had to be found and wrapped with the necessary backticks. -
I had some draft posts, apparently. These get converted by
wordpress-export-to-markdown
without a clear indication that they’re draft. I didn’t want to publish them, but they were fleshed out enough that I didn’t want to delete them either. For each of these I addeddraft: true
to the frontmatter, snaffledeleventy.config.drafts.js
fromeleventy-base-blog
and updatedeleventy.config.js
to use it:Loading...const pluginDrafts = require("./eleventy.config.drafts.js"); module.exports = function(eleventyConfig) { ... eleventyConfig.addPlugin(pluginDrafts); ... };
Step 6. Implement categories and tags
Eleventy uses ‘tags’ to represent the internal collections. Apparently, it’s not taxonomy the way categories and tags
are used in WordPress. To keep these separate, you can create a new collection for them.
Replace frontmatter references to tags
with hashtags
to give them a separate namespace. You can use a regex here.
The following gist contains the collection definitions, their paged equivalents, as well as sample index and individual
pages for each. These have been based on the existing files for posts and tags from the starter.
Step 7. Deployment to Netlify
This was pretty painless. Hook up Netlify to the repo and it’ll trigger a build. The netlify.toml
that came with the
starter probably helped, but it really just took care of itself.
After that, I just had to update the DNS to point to the Netlify deployment, hook up the Lets Encrypt SSL cert through
Netlify and off it went.
Additional changes
-
I replaced the search box in the header with a link to
/search
because I couldn’t get the form to pass the query
param or it otherwise lost it once it reached search page. -
Modified the footer to make the year dynamic using this helper in
eleventy.config.js
:Loading...config.addHandlebarsHelper('currentYear', () => { return DateTime.format(new Date, 'yyyy'); });
-
Some of my images are smaller than the post width, so I added the following blanket fix to
assets/css/main.css
:Loading....prose img { @apply w-full mx-auto; }
If your blog is still active, there’s some other good stuff you can snaffle
from eleventy-base-blog
like RSS feed support. So it’s worth poking
around that starter to see else you might find useful.
Caveats:
This method does none of the optimisation things that eleventy-img
does
because build time took forever, I’m impatient, and I honestly don’t care so much about this right this minute. My
priority is to get the sites off before my VPS expires. If image optimisation is a priority, you should be able to
integrate eleventy-img
possibly with eleventy.config.images.js
from eleventy-base-blog
as a foundation but I don’t know how compatible
that will be with the above, YMMV.
References
- How To Migrate From WordPress To The Eleventy Static Site Generator
- wordpress-export-to-markdown
- vredeburg Eleventy and TailwindCSS starter