DEV Community

Christian Specht
Christian Specht

Posted on • Originally published at christianspecht.de on

Migrating my blog from Jekyll to Hugo

A few weeks ago, I migrated my blog for the third time…after WordPress, Blogofile and Jekyll it’s now powered by Hugo.

There’s nothing wrong with Jekyll. I just became more proficient with Hugo in the meantime (I hope I’m behind the steepest part of Hugo’s learning curve now…) and besides its insane build speed, I like the simplicity of installing/updating Hugo (compared to Ruby/Jekyll) on Windows machines.

And not to forget all the things I learned about processing images with Hugo.

My blog has a few old posts which directly load multiple larger images (e.g. this one). The images are not that large…but still, it would make more sense to auto-generate thumbnails and load just the thumbnails directly with the post. Not sure if it’s worth the effort, I don’t have that many posts with large images yet, but who knows…

Even though this is not the first Hugo site I built, I still learned a few new things about Hugo…and noticed some differences between Hugo and Jekyll:


Some things are easier in Hugo

…like getting the number of posts per year, for example.

Compare the Jekyll and Hugo versions of the “Archives” box in the sidebar. In Jekyll, I had to create logic for this myself, whereas Hugo supports it out of the box.


Hugo template code works ONLY in templates

In Jekyll, I could just create a page (the archive, for example) and write code like this directly in the page:

{% for post in site.posts %}
  <a href="{{ post.url }}">{{ post.title }}</a>
{% endfor %}

Enter fullscreen mode Exit fullscreen mode

Hugo just ignores all template code inside regular pages. To build something like the archive page, you need the actual (empty) page, and a special template which contains all the logic and is used only by that single page.


Render hooks

Hugo has so-called render hooks, which allow things like auto-prefixing ALL image URLs with the complete site URL.

In my old Jekyll site, I created all hyperlinks (in all templates and in all Markdown pages) without domain, e.g. /page.html.

When I switched to Hugo, I decided to use “proper” hyperlinks with full URLs (https://christianspecht.de/page.html). Pasting {{ "/page.html" | absURL }} all over the main template (for the menus etc.) was one thing, but all images in all posts are also linked without domain (example).

But - as noted in the last paragraph - in Hugo it’s not even possible to put template code into regular pages, so I couldn’t just do something like {{ "/image.png" | absURL }}.

Hugo’s solution for this is called Render Hooks (there are more types of them, but I needed the ones for images).

You just need to create one file with what looks similar to a shortcode, and this causes Hugo to render all images with full URLs.

For example, the example Markdown image code linked above looks like this when rendered with this hook:

<img src="https://christianspecht.de/img/git-ssh-2.png" alt="Git Gui screen" />

Enter fullscreen mode Exit fullscreen mode

No HTML comments

Hugo also removes all HTML comments when generating the output, including the ASCII art I have at the top of my blog’s HTML code.

Apparently the only way to get a <!-- --> comment block into a Hugo site is converting it to Unicode, storing it in Hugo’s config file and loading it in the template with safeHTML.


Creating PHP pages is more complicated

My blog contains some PHP pages (for the project pages), and apparently it’s not that easy to generate PHP pages in Hugo.

Jekyll treats all files (as long as they contain YAML front-matter) equally and doesn’t really care about the file extension.

Hugo treats files with different extensions completely different. To create PHP pages with Hugo, I would have to create a copy of all layout files, just with .php instead of .html.

In the end, I decided to “cheat”, generate the project pages as .html files (so they use the same layout files as all other pages), and rename them to .php in the build script.

Top comments (0)