Migrating a Website from WordPress to Hugo

I've made a decision to migrate my website from WordPress to Hugo to improve current page speed and allow for a future with less frustrations and less time spent trying to find tiny bits of performance. First, I've created a new GitLab repository and tried to run a sample Hugo website. After that, I was ready to start the migration process.

Initial Migration

To start our mission to abandon wordpress I wanted to see how much I could migrate automatically.
I've used the most popular migration script, although you can check out more if the first one wouldn't work for you.
For me, It did migrate easily, but sadly not a 100%. I've done the rest with python scripts, replaces with Sublime Text across multiple files at once and a little bit manually.

Migrating From WordPress to Hugo

The script did migrate tags, categories, titles, slugs (the pages URLs), all the images, including dozens of copies with sometimes different resolutions that WordPress or some of its plugins apparently generated.

Each page was converted to a separate .md file that uses a MarkDown format. It is a plain text format that can later be converted to other different formats. For example, Ulysses can output markdown as a PDF or epub, and Hugo converts your MarkDown to HTML web pages.
Plain text is also great for storing in a version control system as you can easily see the changes in each commit or even revert any change. Git and version control systems as a whole are not just for huge teams or big projects.

WordPress and, for example, Microsoft Word use something called 'What You See Is What You Get' (or WYSIWYG) instead, but that means that the actual format is hidden from you and can be stored as an unreadable binary.
This may be good, but even in WordPress, the way you edit your text doesn't fully represent how it will look on the website. Depending on your theme, you would still want to wait for a preview to see what is the final result.

A lot of MarkDown editors have live preview, it's just that you still edit in markdown, and Hugo allows you to view how your actual page will look a lot faster, which I will go into a bit further.
MarkDown Example - My Favorite Games
Here, for example, I've used '#' to make a title and then '-' for a bullet point list. You can look up other options in this cheatsheet.

So now, because you store your posts and pages in a format that isn't hidden from you, you will be able to more easily migrate to something else if support for a specific static generator comes to an end, or you just want to move to something new.
Not saying it isn't possible, but it took me a lot longer to migrate from WordPress to Hugo than it would take me to migrate from Hugo to any other static generator. Also, unlike WordPress, you have control of the images, and you wouldn't make ten copies of your image and use #7 for no reason.

Using the migration script - not all pages converted to pure MarkDown. Some pages still had bits of HTML inside them that the script didn't know how to convert. So I needed to do some polishing work, replacing or removing leftover HTML bits, even though if you enable unsafe = true in the settings, you could just leave it, and the result would mostly be fine.
At this point I’ve also wrapped all the code snippets inside the highlight block:
{{< highlight "C#" >}}
{{< /highlight >}}
This is actually an embedded Hugo feature, you don’t need anything extra to have code-highlithing on your blog.

1// Small example with C# highlighting and extra functionallity when you hover over it
2// for example, to copy all of this.
3var sum = 1 + 2;
4Console.WriteLine(sum);
 
And so, you can just try running this WordPress to Hugo exporter, not doing anything else, and see if there will be any extra work for you. You can quickly add a theme and build the website or create a local server to look at the migration results.

Build & Local Server

One of the Hugo's amazing features for me is that you can easily test almost everything about your website on your machine right after you make any changes.
Ether by building the website or by running a local server.
To build, you would just open the console at your projects folder and type hugo. This will get your website built to the /public/ folder, which for me takes just a second for this website on a decade-old laptop. And now just open /public/index.html with a browser.

Running Hugo on an Old Laptop

But instead of that, I primarily use a local server feature.
You can run it with hugo server and go to http://localhost:1313/ in the browser, and if you make changes to your content, they will be rebuilt automatically, the page will be reloaded. So no need to run hugo after every change, and this automatic-rebuild is a lot faster than a full build because it rebuilds only the parts that change (~10 times faster for me).

Shortcodes

Speaking about removing HTML bits from your .md files, you can't always just remove them or replace them with something available in MarkDown. For example, if you want to embed a youtube video or even just a random iframe, then like I said, if you use unsafe = true, you can just leave the HTML inside your .md files, but it may be better to use shortcodes.
So instead of adding something like

1<div style="position: relative; width: 100%; padding-bottom: 56.25%;">
2<iframe title="Youtube Video" src="https://www.youtube.com/embed/qtIqKaDlqXo" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen style="  position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"></iframe>
3</div>

to each page you want to have a youtube video, you can move the html above to a shortcode file ‘yt.html’ in the folder layouts/shortcodes.
Then just replace the qtIqKaDlqXo  with {{ index .Params 0 }} , this allows you to add any video by only specifying the URL's unique part in your .md files, like this: {{< yt qtIqKaDlqXo >}}  .

If you later want to change the HTML, for example, to add lazy loading to the videos, then you will need only to change the yt.html file, and all of the pages that use your shortcode will regenerate.

Code highlighting I've shown before is also a shortcode, just the one embedded into Hugo itself. Also, a quick tip, something I didn't know initially, is that you can add CSS directly to the shortcodes by adding it inside the <style> </style>  block.

You can find helpful shortcodes I've made during this migration process, like collapsible blocks, text highlighting, and others, on my github repository.

FrontMatter

FrontMatter is extra meta information that can be included in each post and is later used by your theme or Hugo itself. For example, you add your title, tags, categories, and authors there.
So, for example, here is the FrontMatter for a previous post:

 1---
 2title: "Static vs Dynamic Website - Abandoning WordPress"
 3author: 
 4  - Stas Korotaev
 5type: post
 6date: 2021-01-23T16:38:15+00:00
 7featured_image: /static-vs-dynamic-website/assets/featured.png
 8tag:
 9  - hugo
10  - wordpress
11---

Most of it is automatically filled when you run the migration, but you'll still be adding it for new posts.

You can have templates for different types of pages that can already contain some FrontMatter. So, for example, you can have one where the author is already pre-filled, and the date can be set to {{ .Date }} which will be replaced with the current time and date. These templates are called archetypes, not really necessary, but can be helpful.

You can add draft: true there as well, so when you do a release build of your website - these pages will not be included, but you can still preview them by adding -D at the end, so for a local server you would do something like hugo server -D .

Among other things FrontMatter also allows you to set ‘lastmod’, to specify when the content was last modified, which may be displayed by some themes, but is also added to your sitemap.xml, and can be used by search engines

1<url>
2   <loc>https://letsmakeagame.net/game-idea-generator/</loc>
3   <lastmod>2020-12-29T18:54:16+03:00</lastmod>
4</url>

But changing it by hand isn't very user-friendly, so instead, if you are using Git, you can run hugo --enableGitInfo instead of just hugo when you build your website. This way, all your pages will have LastModified update automatically based on your repository git history.

Theme

There are a lot of interesting looking themes that you can browse through here: https://themes.gohugo.io/
A Few Hugo Themes

But to get started, you can add the Ananke theme with a few steps.

I've looked at a lot of these themes even before choosing Hugo. I didn't want to create my own at the moment, so I wanted to have some that would be somewhat similar, with the main page being a list of posts, with a top menu and a custom block on the side.
I didn't find exactly what I wanted, so I decided to get the one I liked the most and modify it a bit.
I guess the main difference is that I've added big featured images, similar to what I had on the WordPress theme.

Adding Featured Image To Posts

I've also made it a bit more extensible to allow me to add new blocks without changing the theme itself again. I’ve done this using partials. This functionality is very similar to shortcodes, and you can use partials inside the theme with something like {{ partial "hooks/sidebar-end.html". }}, that the user can then override by creating a layouts/partials/sidebar-end.html, which is near your shortcodes folder. This way, if I want to replace my theme later, I'll quickly reuse my partials.

To change the theme, I've made a fork on github, this way, I can later still try and get the latest changes from the original repository that fix some bugs or introduce new ones functionality.

Migration Complete

The migration itself and theme-related things didn't take long, and the website was technically ready to be launched. But I still wanted to find a new hosting better suited for static websites. Also, when looking at PageSpeed, I've noticed that performance wasn't ideal due to the default Disqus version being too slow, so I also wanted to improve that instead of just disabling comments.
And finally, there were still a few small things missing that I've added with various plugins over the years to WordPress, and I wanted to add them to Hugo version before switching over as I wasn't in a hurry, and writing shortcodes and partials was simple and fun.

← PrevNext →
Static vs Dynamic WebsiteHosting for a Static Website (Coming Soon)
Get Notified of the Next Posts

Sharing is caring!


You may also like:

Related Content: Static vs Dynamic Website - Abandoning WordPress

Static vs Dynamic Website - Abandoning WordPress

Related Content: Social Media Share Buttons for a Hugo Website

Social Media Share Buttons for a Hugo Website

Related Content: Making Web Tools for Games with Unity & WebAssembly

Making Web Tools for Games with Unity & WebAssembly