Roberto's blog

From Gatsby to Hugo: a Markdown minimalist's journey

I have been using Gatsby for my blog and I have never been fond of it’s complexity considering that all I need is Markdown rendering. Trying to update Gatsby and its dependencies to the latest version was painful enough to motivate me to jump ship. After looking at what’s out there, I settled for Hugo. As I will inevitably forget all about it in a month’s time, I am writing this post to remind myself of how the whole thing works.


Hugo is a static site generator which uses the structure of the source content to organize the rendered site. The content of the site is stored in the content directory:

└── content
	├──  // <-  (homepage)
	└── blog  // <- section
		├── // <-  (this post)
		└── // <-  (section index)

The folder is structured in sections and pages. A section is a folder that holds content, like the blog folder. A page is simply a Markdown file that gets rendered to HTML, like blog/

A Markdown file can represent a single page or a list page. For example, blog/ is a single page (the very same page you are reading right now). In contrast, blog/ is a list (or index) page used to render the list of posts. Hugo also uses a top-level Markdown file, defined at the root of the content/ folder, to render the homepage of the site.

All Markdown files in content/ have a type associated with them. By default, the type is the name of the subdirectory that the file resides in. For example, the type of blog/ is blog. Types determine how the content is rendered with templates, more on this later.


The layouts/ directory contains the templates for the site used to render the Markdown files. Templates use variables, functions, and methods to transform the content into an HTML page using Go’s html/template package. Here is a simple template that renders the content of a Markdown file:

{{ define "main" }}
{{ .Content }}
{{ end }}

The Content method on a Page object renders Markdown to HTML. In practice, the most important templates are the single and list templates, which respectively are used to render single and list pages. The homepage of the site (content/ has its own template file layouts/index.html:

  index.html      // homepage template
    single.html   // single page template
    list.html     // list page template

Hugo looks in the _default/ directory if it cannot find a more specialized template to render a Markdown file. To use a specialized template for a specific content type, you need to create a subdirectory inside layouts/ with the name of the type and add the template there. For example, this is how you can use a seperate template for single blog pages.

    single.html // specialized template for single pages in the blog folder

If every template was defined on their own, there would be a lot of repetition for common bits such as the footer and header. So you can define a base template which other templates inherit from (layouts/baseof.html):

<!DOCTYPE html>
    <meta charset="utf-8">
    <title>{{ block "title" . }}
      <!-- Blocks may include default content. -->
      {{ .Site.Title }}
    {{ end }}</title>
    <!-- Code that all your templates share, like a header -->
    {{ block "main" . }}
      <!-- The part of the page that begins to differ between templates -->
    {{ end }}
    {{ block "footer" . }}
    <!-- More shared code, perhaps a footer but that can be overridden if need be in  -->
    {{ end }}

Static assets

The static/ directory is pretty straightforward. All assets placed here can be used throughout the project.



There is a lot more to Hugo than the concepts explained in this posts. Even so, this is pretty much all you need to create a very simple blog and most of it comes pre-configured using an off-the-shelf theme.