I totally love Hugo. It’s a fast static site generator – and I really love static sites!

But it takes more effort to post content on one of my Hugo sites, which adds up to a lot of time and effort when you’re posting a lot of content, so I decided to migrate it to WordPress. WordPress , together with a few plugins and custom scripts, greatly simplify and speed up the posting process than Hugo with various tools. The WP site will still be a static site hosted on Netlify or Cloudflare Pages, so I won’t be losing anything on the hosting and server side of things.

There’s definitely some room to improve my Hugo workflow by getting some new tools, but it’s still going to be more effort than just using WP alone. I’ll still use Hugo for my other less active sites.

In looking for guides for the migration, there’s not a lot of content out there on moving from Hugo to WordPress, but there is a bunch for the other direction. I did find this post which helped me get started: From Hugo to WordPress. So, this is how I did it…

Make an RSS feed and using the WordPress RSS Importer

First, I needed to get each post’s full content to the Hugo RSS Feed. The built-in Hugo feed doesn’t provide the full content, so following the advice and content from here https://discourse.gohugo.io/t/full-text-on-rss-feed/19009, I made my own custom RSS template.

The WordPress RSS Importer imported without issue but I saw that every time the RSS feed had a new line ( or “\n”), the WP importer made an “n” on it’s own paragraph. I ended up with posts with lot’s of n’s. So in my RSS feed, I updated the line from

{{- .Content -}}

to

{{- replace .Content "\n" "" -}}

Categories and Tags

Next, I wanted categories to import as well. I added the following within the in the RSS template:

{{ range $i, $name := .Params.categories }}{{ if $i }}, {{ end }}{{ $name }}{{ end }}

This worked perfectly well for categories. I wanted the same thing for tags and added similar code to the RSS feed:

{{ range $i, $name := .Params.tags }}{{ if $i }}, {{ end }}{{ $name }}{{ end }}

But there’s a problem, the RSS Importer doesn’t support tags. So, let’s modify the RSS Importer plugin!

Please note that the version of the plugin I did this on is version 0.3.2. Also, am only migrating the site once, so I don’t care if my edits get overwritten with a plugin update.

Open up the plugin folder (rss-importer) and edit the rss-importer.php file. Look thru the file for the get_posts() function. We’re looking for the part where the code parses thru the RSS XML content, looping thru and posts. After the section for categories, which ends on line 114 in v 0.3.2, add the following code:

preg_match_all('|(.*?)|is', $post, $tags);
$tags = $tags[1];

if (!$tags) {
     preg_match_all('|(.*?)|is', $post, $tags);
     $tags = $tags[1];
}

$tag_index = 0;
foreach ($tags as $tag) {
     $tags[$tag_index] = esc_sql( html_entity_decode( $tag ) );
     $tag_index++;
}

The code mirrors what the plugin does for categories, but we changed it for tags.

Further in the function, before the foreach loop ends, there is a line where all the variables are saved together. Edit this and add tags, so it looks like:

$this->posts[$index] = compact('post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_status', 'guid', 'categories', 'tags');

Now we want the plugin to save the tags, so keep going down the plugin code for the import_posts() function. Just before the lines where categories are saved, which is this:

if (0 != count($categories))
     wp_create_categories($categories, $post_id);
_e('Done!', 'rss-importer');

…we need to add our code to save tags. But there’s no tags counterpart for wp_create_categories(), so let’s use another function that can be used for all taxonomies (but defaults to tags). So add this before the categories code above:

if (0 != count($tags))
     wp_set_post_terms($post_id, $tags);

Featured Images

The last thing I needed was for featured images to be imported too. But this isn’t built into the RSS importer, so let’s make it happen!

It’s simple enough to add the featured image to the RSS output and it should be easy enough to use media_sideload_image() to upload the image, and add it as the featured image. However, am working with WP locally and just couldn’t get it to work – maybe what was said here https://gist.github.com/saas786/a567363477df7e28b0c1df6295cadf75 is right in that some things with WP doesn’t work locally, but even the fix provided didn’t work for me. I tried all sorts of different functions and fixes to make the plugin upload images but couldn’t get it to work.

Fortunately for me, the filenames of all my featured images are the same as each post slug, so I decided to do this another way. 1st, I uploaded all the featured images into WordPress. Then I wrote a small script that would match the post to the uploaded image and set the featured image to each post. Add this to a plugin or to a theme’s functions.php and let it run once…

global $wpdb;

$posts = $wpdb->get_results("
     SELECT post.ID thepost, img.ID theimg
     FROM $wpdb->posts post
     LEFT JOIN $wpdb->posts img
     ON post.post_name = img.post_title
     WHERE post.post_type = 'post' AND
     img.post_type = 'attachment'
");

foreach ( $posts as $p ) {
     set_post_thumbnail( $p->thepost, $p->theimg );
}

Some SEO Stuff

Adding a plugin to help with SEO is a no-brainer. Also, getting URLs for posts, categories, and tags to stay the same with WP is simple enough to change in the permalinks settings.

And that’s it, content migration from Hugo to WordPress is done!