Afirst approach to Hugo for Jekyll developers

New concepts and equivalences

  • Published: August 4, 2018


This is a list of differences between Jekyll and Hugo concepts.

Markdown processors

Most of the hard work is 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

#Jekyll (kramdown)Hugo (Blackfriday)
Markdown processorLiquidBlackfriday

Hugo’s templates use the following Go libraries:

    • Package template (html/template) implements data-driven templates for generating HTML output safe against code injection.
    • Package template implements data-driven templates for generating textual output
    • ”“Actions”–data evaluations or control structures–are delimited by “{{” and “}}”; all text outside actions is copied to the output unchanged..”

In Jekyll, templates use Liquid, which has the {% %} operators, but in Hugo we need to adjust them to the {{ }} “equivalent”, e.g.: the for iterator equivalent would be range function.

#Jekyll (kramdown)Hugo (Blackfriday)
template{% %}{{ }}

The dot

When migrating your Jekyll layouts you have to remember to pass the context to functions {{ template "something.html" . }} Pay special attention to the above . at the end

Execution of the template walks the structure and sets the cursor, represented by a period '.' and called "dot", to the value at the current location in the structure as execution proceeds.

That means that you will have your current context in the {{.}} variable and access current Page properties and method from it, like {{.Title}}.

Also note that current Page can be a Page or Section depending on the location of the content that access it.

Includes and partials

Jekyll includes have their includes equivalent in Hugo as partials when designing a template.

There are also shortcodes to include code in posts, but in contrast to Jekyll, to include content in templates you need to use partial templates.

A shortcode is a simple snippet inside a content file that Hugo will render using a predefined template.

#Jekyll (kramdown)Hugo (Blackfriday)
Include code{% include "snippet.html" %}{{ mysnippet . }}
Include content{% include "snippet.html" %}{{ partial "snippet.html" . }}

Template operators

Hugo templates logical and comparison operators and its equivalences in Jekyll.

#Jekyll (kramdown)Hugo (Blackfriday)
does not equal!=ne
greater than>gt
less than<lt
greater than or equal to>=ge
less than or equal to<=le
condition A or condition B<=or
condition A and condition B<=or
if not A{% unless A %}not A

Site structure

WYSIWYG structure

The site structure is reflected in the website structure by default.

Hugo believes that you organize your content with a purpose. The same structure that works to organize your source content is used to organize the rendered site.

The structure can be changed through front-matter.


Articles and posts goes into /content, each directory at content/<DIRECTORIES> are special in Hugo and are called Content Sections.

Each section can have nested directories, but all the content inside them, will have the same base section, (i.e. the first directory under /content).

Bonus - Inspect equivalent

There is a helpful {{ <var> | inspect}} filter tag in Jekyll that allows to print any variable in a template. The closest equivalent in Hugo is the usage of {{ print "%#v" <var> }}. 1

To print the current Page data {{ printf "%#v" . }} will print:

  • the top level object and
  • object fields

E.g.: for a Section in /content/communication it will print something like:

&hugolib.PageOutput{Page:(*hugolib.Page)(0xc4210f2a00), paginator:(*hugolib.Pager)(nil), paginatorInit:sync.Once{m:sync.Mutex{state:0, sema:0x0}, done:0x0}, targetPathDescriptor:hugolib.targetPathDescriptor{PathSpec:(*helpers.PathSpec)(0xc4200ca240), Type:output.Format{Name:"HTML", MediaType:media.Type{MainType:"text", SubType:"html", Suffix:"html", Delimiter:"."}, Path:"", BaseName:"index", Rel:"canonical", Protocol:"", IsPlainText:false, IsHTML:true, NoUgly:false, NotAlternative:false}, Kind:"section", Sections:[]string{"communication"}, BaseName:"_index", Dir:"communication/", LangPrefix:"", URL:"/communication/", Addends:"", ExpandedPermalink:"", UglyURLs:false}, outputFormat:output.Format{Name:"HTML", MediaType:media.Type{MainType:"text", SubType:"html", Suffix:"html", Delimiter:"."}, Path:"", BaseName:"index", Rel:"canonical", Protocol:"", IsPlainText:false, IsHTML:true, NoUgly:false, NotAlternative:false}}


Hugo has a lot of new concepts that should be addressed before migrating a Jekyll website, these are just a set of notes of some of the differences hoping to ease the understanding of main docs concepts.


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 ·