How I migrated this website articles from Jekyll to Hugo

Portrait photograph of Victor Hugo
Portrait photograph of Victor Hugo (License: Public Domain)

So you moved your Jekyll posts to Hugo, what's next?

  • Published: August 4, 2018

Overview

After having created a new hugo website hugo new site my_new_web and developed a new theme, I copied all the posts of my old Jekyll instance to the /content directory.

This is a list of issues that I needed to adjust in Jekyll posts to fit the new Hugo structure and concepts.

Most of the issues were due to the differences between Kramdown and Blackfriday, which are the markdown processors used by default by each program.

kramdown is a free MIT-licensed Ruby library for parsing and converting a superset of Markdown.

Blackfriday is a Markdown processor implemented in Go

Remove posts folders

You can now get rid of the old and ugly _posts folder, Hugo handles it beautifully having the indexes files at the same level of the posts.

# Jekyll (kramdown) Hugo (Blackfriday)
Posts location /docs/emacs/_posts/2018-08-08-best-editor-ever.md /content/docs/emacs/2018-08-08-best-editor-ever.md

use index.md or _index.md

Hugo has the concept of Page Bundles, there may be index.md and _index.md files in Hugo, depending on its role:

  • Leaf Bundle
    • A directory in the last position of the /content tree that contains an index.md file (note the absence of _) that may contain images or associated documents at the same directory level.
  • Branch Bundles
    • A directory withing the /content tree, that contains an _index.md and may contain more subdirectories.
# Jekyll (kramdown) Hugo (Blackfriday)
index /docs/index.md /content/docs/_index.md
simple post /_posts/2018-08-09-my-post.md /content/my-post.md
post with images (or docs) /_posts/2018-08-09-my-post.md
/assets/img/photo.jpg
/content/my-post/index.md
/content/my-post/photo.jpg

Filenames doesn’t require dates

As regular Jekyll posts, filenames included the typical date prefix as part of the filename because it was born as a blogging platform. For me this was never something I wanted to have in filenames, now you can have the date in front-matter.

Newer Hugo versions are able to process these filenames but I prefer to get rid of them completely and add them as metadata in front matter, e.g.: date: 2018-08-09.

Jekyll also incorporated collections that doesn't require dates in filenames, but most of the plugins are made for posts not collections, so you would probably have posts.
# Jekyll (kramdown) Hugo (Blackfriday)
filenames /_posts/2018-08-09-uruguay.md /uruguay.md

Add aliases of old URLs

You old Jekyll URLs may differ from the new website structure, if this is the case use aliases key in front matter with a list of the old URLs that should redirect to this new post.

IALs not supported

Blackfriday doesn’t support IALs.

One of the features I missed more from Kramdown was the absence of block inline attribute list. They are very handy to quickly assign classes to a paragraph for example.

Your posts are probably full of {: .myclass} or {: class="myclass"}.

In Blackfriday you have two choices:

  • manually write HTML
  • Create a shortcode to assign a paragraph a custom class

I prefer to manually write HTML as creating a shortcode for this task creates more noise than writing plain HTML.

It also gives you greater control to apply styles like Bootstrap classes.

# Jekyll (kramdown) Hugo (Blackfriday)
IALs Lorem Ipsum
{: class="info-alert"}
<div class="info-alert">Lorem Ipsum</div>
Lorem Ipsum
{: class="info-alert"}

Detecting IALs

An easy approach for detecting them is to look for {: in your posts folder (e.g.: docs or _posts) with grep: grep “{:” -R docs


$ grep "{\:" -R docs
docs/lisp/emacs/_posts/2017-06-23-jump-to-function-definitions-in-emacs.md:{: .alert .alert-info}
docs/lisp/emacs/_posts/2017-06-17-writing-alternating-between-english-and-spanish-in-emacs.md:{: class="blockquote"}

Remove markdown inside HTML tags

In Kramdown you can have *foobar* or [foo](http://example.com) embedded in an HTML <div> </div> block and they would be converted to their respective HTML equivalents: <em>foobar</em> and <a href="http://example.com>foo</a>".

In Blackfriday, if you start writing HTML, then everything inside that block should be valid HTML.

# Jekyll (kramdown) Hugo (Blackfriday)
Markdown inside HTML <div>*foobar*</div> <div><em>foobar</em></div>

Detecting code inside HTML tags

This is a little trickier than before as you should not check the unprocessed posts because it will contain all the markdown outside and inside HTML blocks.

In this case you can check them at the final site, i.e.: in the public folder after building the website with $ hugo.

Then you can use grep to find unprocessed markdown elements:

  • __ and **: the strong HTML element
  • ](: part of the markdown link notation [foo](http://bar)

Looking for _ or * would be very difficult, so I chose to leave them there as they don’t look so bad anyway.(may be just a little SEO drawback as not being natural words from English or any other language)

You can figure out more patterns to look for at the final website too, like *backticks*.

So we can run $ grep “](” -R public to detect markdown links in the public, and grep “**” -R public to detect unprocessed emphasis markdown code:


$ hugo
$ grep "](" -R docs
public/writing-in-emacs-checking-spelling-style-and-grammar/index.html:  answer to [How to add a hook to only run in a particular mode?](https://stackoverflow.com/a/6141681/1165509)
public/lisp/index.html:- [Common Lisp - A gentle introduction to symbolic computation]({% link _books/common-lisp-a-gentle-introduction-to-symbolic-computation-david-touretzky %}) by David Touretzky

$ grep "\*\*" -R public
public/lisp/emacs/jump-to-function-definitions-in-emacs.html:which is bound to **xref-find-definitions** and it will look for the

Removing quotes

In Jekyll (Kramdown), blockquotes can be started with the > marker, and it will generate <blockquote></blockquote> tags.

In Blackfriday, as with IALs, we can have blockquotes by manually setting them up in plain HTML or with shortcodes.

Again I used plain HTML so I had greater control to apply Bootstrap classes and organize its content.

# Jekyll (kramdown) Hugo (Blackfriday)
blockquote > To be
> or not to be
<blockquote>to be or not to be</blockquote>

Detecting blockqoutes

To detect unprocessed blockquotes look for lines starting with &gt; in the Jekyll posts folder.


$ grep "^>" -R _posts_
docs/php/joomla/_posts/2017-01-26-find-out-if-your-joomla-site-has-been-hacked.md:> Compares the timestamp 
docs/python/language/_posts/2016-05-30-python.md:> The rule of thumb is

Why not use a migration tool

Hugo comes with a handy hugo import jekyll command but there were two issues that made me avoid it:

  • Jekyll flexibility
  • Jekyll fast development iterations

Flexibility

Jekyll is very flexible, and that flexibility has a price when migrating to another static website generator.

Chances are that your website doesn’t look like a standard Jekyll instance, i.e.: just a project with a _posts_ directory at root with posts in YYYY-MM-DD-title.md format.

In my case I use collections, data files, and custom layouts that get information from them. All custom is impossible to cover beforehand by any migration tool, so a manual migration is my choice.

Rapid development

Jekyll, as Hugo does, moves forward fast, and you are probably continuously adopting new ways to do things and incorporating them into your toolbox.

A migration tool is something that you do once and forget about it, migration scripts doesn’t get much involvement so they are probably not in sync with other distribution status, i.e.: with your website design.

Summary

Putting it all together:

# Jekyll (kramdown) Hugo (Blackfriday)
Posts location /docs/emacs/_posts/2018-08-08-best-editor-ever.md /content/docs/emacs/2018-08-08-best-editor-ever.md
index /docs/index.md /content/docs/_index.md
simple post /_posts/2018-08-09-my-post.md /content/my-post.md
post with images (or docs) /_posts/2018-08-09-my-post.md
/assets/img/photo.jpg
/content/my-post/index.md
/content/my-post/photo.jpg
filenames /_posts/2018-08-09-uruguay.md /uruguay.md
IALs Lorem Ipsum
{: class="info-alert"}
<div class="info-alert">Lorem Ipsum</div>
Lorem Ipsum
{: class="info-alert"}
Markdown inside HTML <div>*foobar*</div> <div><em>foobar</em></div>
blockquote > To be
> or not to be
<blockquote>to be or not to be</blockquote>

Conclusion

Migration can be time consuming depending on the amount of features you used from Jekyll and more specifically kramdown, if you just had a simple Jekyll website with plain markdown posts, moving to Hugo should be straightforward.

References

Uruguay
Marcelo Canina
I'm Marcelo Canina, a developer from Uruguay. I build websites and web-based applications from the ground up and share what I learn here.
comments powered by Disqus
Except as otherwise noted, the content of this page is licensed under CC BY-NC-ND 4.0 ·