Migrating my blog from WordPress to Hashnode

Subscribe to my newsletter and never miss my upcoming articles

A step-by-step guide for migrating a blog from WordPress to Hashnode.

Why did I switch from WordPress to Hashnode?

Currently, my portfolio website and blog are on WordPress. It's been really great for search engine optimization, and the experience with WP Engine has been almost flawless, but it's been a long time since I built it so everything I use to run it locally is broken: Vagrant, Scotchbox (LAMP stack), WP Distillery (Wordpress-flavoured LAMP), etc. It's no longer as quick as it used to be to make small changes.

I have been evaluating different blogging platforms. I considered Medium, but I am not a fan of the experience of landing on a Medium link and hitting a paywall, so I didn't want users to experience that if they find one of my posts. It also didn't have any ads injected, so that's great.

I also thought about using a headless CMS and a static site generator like Nuxt as I've had a great experience with it, but that would've taken a lot of dev time to build. I think that's something I would do for the rest of my site once I have more time.

Hashnode has a variety of features that I appreciate:

  • Markdown editor
  • Canonical link support so I can link to my original post if I wanted to
  • Automated backups to a Github repository
  • Custom domain linking
  • Nice code syntax highlighting, with the bonus of embedding external services for code snippets

What's great is that these are all free, too. There's also a helpful community on Discord to get some help if needed.

Walkthrough for migrating from WordPress to Hashnode

Download post content

To begin the migration, you'll need to download your existing posts. By default these are in XML format. Since I don't really like working with XML, I used the plugin WP Import Export Lite to export my posts as JSON.

wp-import-export-lite.png

The resulting output was an array of objects that had the following shape.

{
  "ID": "463",
  "Title": "iTerm colour schemes",
  "Content": "Introducing 2 iTerm colour schemes [...]",
  "Excerpt": "iTerm colour schemes based off of my Visual Studio Code theme",
  "Date": "June 22, 2020",
  "Post Type": "post",
  "Permalink": "https:\/\/tinaciousdesign.com\/blog\/iterm-colour-schemes\/"
}

Download images

Next, you'll need to download all your images. There's more than one way you can approach it:

  1. Download all of the images uploaded to your WordPress install
  2. Programmatically process a JSON or XML export of the Media Library, which has links to the media

The first option is by far the easiest, so I went with that.

Images are stored in the ./wp-content/uploads directory by year and month.

wp-content/
└── uploads
    ├── 2019
    │   ├── 01
    │   ├── 02
    │   └── 03
    └── 2020
        ├── 01
        ├── 02
        └── 03

Your posts reference images like this:

https://example.com/wp-content/uploads/2020/08/filename.png

You can use an SFTP tool like Cyberduck or FileZilla to download this folder.

Upload images elsewhere

That "elsewhere" I chose is Fast.io because it had a generous free plan that leverages a variety of storage options like Dropbox, Google Drive, OneDrive, etc.

I can take advantage of one of those amazing storage services and link it to Fast.io. I chose Dropbox.

After manually deleting some images that weren't used in the blog posts, I uploaded the entire contents of the downloaded ./wp-content/uploads directory to Dropbox.

Programmatically publish posts to Hashnode using their API

Hashnode has a GraphQL API. It's somewhat limited in functionality but has most of the basic features. There's a helpful blog post on how to use it.

Back-dating posts is not supported in the API so I needed to do this manually for each of my posts, which was tedious.

I've created a repository of the scripts I used to facilitate cleaning up and bulk uploading posts.

Conclusion

Overall, I was happy with my migration to Hashnode. It didn't take very long. Hashnode has a lot of features I look forward to using and I was happy to invest the time in migrating.

Setting up my custom domain was a bit painful compared to other services I've set up domains with, e.g. Heroku, Netlify, Github Pages, Amazon S3. I use Cloudflare, and eventually what worked was contacting support on Discord, disabling Cloudflare's default proxying behaviour, and waiting until the SSL certificate was re-generated. Within a day it was working, so the trouble was only a minor setback.

The authoring experience is nice, typographically. There's also support for a lot of different types of embeds. Showing code is pretty without any effort on my part, and their embeds support the services I use most (Codepen, Codesandbox).

Overall, migrating to Hashnode is a relatively low investment, low risk migration.

If you like the sound of this and want to migrate your WordPress blog to Hashnode, you can use some handy scripts I created.

Sandeep Panda's photo

Hi 👋 welcome to Hashnode!

We are going to revamp our APIs and document them properly this week.

I think it took some time to issue the certs because there were too many failed validations for your domain since Cloudflare was not bypassed after adding DNS records. We are making some changes to our platform which will help us catch these mistakes early on and prompt our users to correct the config.

Show +5 replies
Sandeep Panda's photo

Hey Tina Just checking - is the problem resolved? If not, happy to dig deeper. :)

Tina Holly's photo

Sandeep Panda Thanks for checking in. It looks like if I copy/paste it multiple times, it works subsequent times. Perhaps it's a redirection or timeout issue. I've only tested on Facebook. It's sufficient for now, I'll just keep it in mind to try multiple times if it doesn't show the first time. Thanks for verifying.

Bolaji Ayodeji's photo

This is really amazing, I'm really glad to see that you're happy with the migration to Hashnode 🤩

Thank you for sharing the process. I'll be happy to get some feedback on your experience so far and some functionalities you think we should add to our APIs.

Show +2 replies
Tina Holly's photo

Bolaji Ayodeji that’s so great to hear!

Also adding the ability to query my posts and get more than just 6 items. Pagination isn’t documented in the GraphQL API for the query user —> publications —> posts. Once your team adds support for historical post dates in the API, I’d like to query my Hashnode posts, match them with my old post data, and update the published date so that it’s accurate.

Bolaji Ayodeji's photo

I've noted this Tina Holly, once we release the new version, we'll ensure to document officially.

Thank you once again :)

Edidiong Asikpo's photo

Welcome to Hashnode. I can't wait to read your articles.

Vamsi Rao's photo

This is really helpful for the community. Thanks for sharing!

Tina Holly's photo

Thanks Vamsi Rao! I was looking for such an article when I started and didn’t find one so I thought I’d make one after figuring it out! 🙃