Hugo is a popular static site generator written in the Go programming language. It is a tool that builds websites from your content files defined as simple text files (usually Markdown, but also Emacs Org Mode or AsciiDoc). The you run hugo build, and it generates a folder of plain HTML, CSS and JavaScript files ready to be hosted on the internet.

Installing Hugo

In macOS, you can install Hugo using Homebrew.

1
2
brew update
brew install hugo

Creating a new site

You can use the following command to start a new Hugo site project:

1
2
hugo new site my-site
cd my-site

Creating the first post

You can create a new post manually or using Hugo CLI.

  • Manually: You just need to create a new .md file inside content folder. Later, manually write the front matter which is a special syntax to tell hugo some information/metadata about the post.
  • Using the Hugo CLI: Alternativelly, you can create a new post using the CLI, use the following command: hugo new content content/posts/post-1.md.

Grouping posts

To create a section or collectino of posts, you can just create a new folder inside content. For example, lets create 3 posts and group them inside the posts folder:

1
2
3
hugo new content content/posts/post1.md
hugo new content content/posts/post2.md
hugo new content content/posts/post3.md

Post ordering

Hugo’s default fallback order for content lists is first by weight, then by date if weights are equal or zero. Setting a weight overrides the date priority for explicit placement, while unweighted posts fall back to date sorting.

Using markdown to create content

Hugo has Markdown support out of the box. By default, Hugo uses the Goldmark Markdown processor which is fully CommonMark compliant.

Reference:

Serving Locally

You can run hugo locally with a simple hugo server command. Use --buildDrafts flag to include posts with draft: true in the front matter.

1
hugo server --buildDrafts

Hugo Machinery

Matching content to template

Hugo scans content/ directory and turns each file or folder into a page. Each file inside content/ has a kind value that Hugo engine assigns automatically.

Content path or itemHugo kindMatching template
content/_index.mdhomehome.html
content/posts/listlist.html
content/posts/post1.mdpagesingle.html
tagstaxonomy and termtaxonomy.html
a specific tagtaxonomy and termterm.html
N/AN/Abaseof.html

A file called _index.md means “this is a list page,” while a regular file like index.md or post1.md is an individual page (kind is page) that matches the single.html template.

You can overwrite the kind of a markdown file by setting layout "recipe" in front matter, Hugo tries layouts/posts/recipe.html and layouts/recipe.html before the defaults.

Hugo checks your project’s layouts/ folder before the theme’s layouts/ folder for each candidate.

If the file lives at content/posts/hello.md. Hugo takes the first folder under content/ and calls it the section. Unless you override it, the section name becomes the page’s type. If the file were content/blog/hello.md, the type would be blog, and Hugo would look in layouts/blog/ instead. You can override this with type: “article” in front matter. Then Hugo would look in layouts/article/, even though the file is in posts/.

The template filename comes from the page’s kind, and Hugo has a fixed vocabulary for this:

  • single.html is for a kind of page, meaning one individual piece of content (hello.md).
  • list.html is for kinds that are collections: sections, taxonomies, terms. It displays a list of other pages.
  • home.html / index.html is for the home page.

Hugo decides hello.md is a page because it’s a regular content file, not an _index.md. A page renders with single.html because that is the rule Hugo hard-codes: one page, one template, named “single.” There’s nothing more to it than convention.

When you download a theme, like PaperMod, it ships with a complete set of templates, including the home layout, list layout, single-page layout, and so on. Its partials are then called by those layouts.

Taxonomy

A taxonomy in Hugo is a way to classify and group your content.

The taxonomy.html template renders the page that lists all terms of a taxonomy, such as /tags/ or /categories/.

You define the taxonomies inside hugo config file. Then hugo will render the taxonomy for each entry of the config.

If you have no taxonomy.html, Hugo falls back to list.html, which often works if it loops over .Pages.

Template language syntax

.RelPermalink returns the page’s relative URL: the path without the domain. It’s what you normally use for internal links.

.Pages

If the same list.html gets used for several kinds of pages, and .Pages means something different in each.

So you need different template files like list.html, taxonomy.html, single.html.

Layouts

Layout is the directory where all the Hugo templates will be stored. You have root/layout and root/themes/PaperMod/layouts. The first has higher priority that the second, this allow you to override themes default template files.

Templates

Templates are the files that decide what the HTML looks like. Hugo takes your content and pours it into a template.

Templates use variables, functions, and methods to transform your content, resources, and data into a published page. Hugo engine will populate those variables, so that your templates files can use to build the HTML page.

For example:

1
2
<h1>{ { .Title }}</h1>
<div>{ { .Content }}</div>

Hugo replaces { { .Title }} and { { .Content }} with the actual title and body from your Markdown file.

Engine under the hood

Hugo uses Go’s text/template and html/template packages.

Multiple output formats

While HTML templates are the most common, you can create templates for any output format including CSV, JSON, RSS, and plain text.

Reference:

Shortcodes

Shortcodes are templates that you call explicitly from inside content, for things Markdown has no syntax for.

A Hugo Shortcode is a type of template that you can invoke from the content markup file.

Some shortcodes are built-in. For example the figure shortcode, which allow you to insert images from markdown.

Shortcodes lives under layouts/_shortcodes directory.

Reference:

Partials

Partials are reusable template fragments called from other templates, never from content. It is the Hugo way of allowing you to modularize templates files. It is a piece of a layout (HTML) that lives in its own file. Later, you can import the partial from the layout itself. This allow you to expand the capabilities of the site generator, like for example, adding support to LaTeX syntax. If you analyse the PaperMod theme, you will see that it imports several partials.

Variables

When Hugo’s parser meets some link inside your markdown, it extracts the pieces and hands them to your template as variables: .Text, .Destination. Title, .Page. This happens for several other objects inside your content file.

Render hooks

Render hooks override how Hugo turns standard Markdown into HTML. You don’t change your content at all.

You add a template such as layouts/_markup/render-link.html, and every Markdown link, image, heading, code block or blockquote in the site then goes through it.

The name render-link.html is a fixed convention. Hugo has a set list of hook names, one per Markdown element:

  • render-link.html
  • render-image.html
  • render-heading.html
  • render-codeblock.html
  • render-blockquote.html
  • render-table.html

Use them when you want to change the output of syntax you’re already writing, for example adding target="_blank" to external links, wrapping images in

, or adding anchor links to headings.

Reference:

Hugo Directory Structure

Reference:

Troubleshooting

When things go wrong, try the following commands to clean cache.

1
2
3
hugo --cleanDestinationDir
hugo server --disableFastRender
rm -rf ./public && hugo server  --panicOnWarning --ignoreCache --disableFastRender --cleanDestinationDir

You can also fully delete the ./public folder, which is generated when you run hugo build or hugo server.

Themes

Hugo uses template files to be able to transform your markdown content into HTML webpage. You can download a collection of templates, aka a theme, from the internet or try to build your own.

Inside each template file, you have access to the Hugo's template language that allow you to structure the markdown content over the HTML structure.

PaperMod theme

Installing

We can just clone the theme repository to themes/<theme-name> folder created by Hugo when you initialized the project.

1
git clone https://github.com/adityatelange/hugo-PaperMod themes/PaperMod --depth=1

Then, you need to update the hugo.yaml file to tell hugo to use the installed theme:

1
theme: ["PaperMod"]

Hacking/mods/addons

Overriding Theme Files

You can override a theme feature by copying the relevant file from the theme’s layouts folder into your project’s layouts folder.

Remember you cloned the PaperMod repository into ./themes/ folder. But you want to avoid modifying the original theme files. So you copy any file from that folder into the ./layout folder. Hugo will prioritize the file in your project’s layouts folder over the corresponding file in the theme.

For example, if you want to modify index.html, copy themes/foo/layouts/index.html to layouts/index.html.

I copied extend_head.html to ./layouts/_partials/extend_head. That file is imported by PaperMod inside the <head> element. In that file I added support to TeX, Google Analytics, etc.

See Override a Hugo Theme for more information.

Modifying CSS

You can modify the CSS used by the theme to adjust things like the font-family of the <h1> tag, or the letter spacing used by paragraphs.

One way of doing that is by modifying the extended_head.html partial file to include a custom CSS file which will overwride a specific CSS selector used by the theme.

You can store the custom.css file inside the static folder: ./static/custom.css.

To make things even more fun, you can get the filename from the Hugo config file!

Add a new entry into hugo.yaml

1
2
params:
  custom_css: ["custom.css"]

Then, modify the extended_head.html to import the file. Avoid modifying the original themes files at themes/PaperMod/layouts/partials/header.html, instead copy it to the layout folder to keep the original theme file intact.

Then you can read the value from the Hugo configuration file using the following syntax:

1
2
3
{{ range .Site.Params.custom_css -}}
    <link rel="stylesheet" href="{{ . | absURL }}">
{{- end }}

Syntax Highlight

By default, Hugo applies syntax highlighting using inline CSS styles.

To use an external CSS file instead, set noClasses to false in your configuration, then generate the stylesheet with the hugo gen chromastyles command.

Hugo’s markdown renderer, Goldmark, detects code blocks, like the ones inside triple backticks, and send them to Chroma. Chroma by default wraps each code object with HTML embedded CSS. So a variable in your code is wrapped by a CSS class that can later be styled using CSS.

Each token gets a short class name, and the actual color is defined once in an external stylesheet

PaperMod vs Hugo

By default, the hugo-PaperMod theme uses Highlight.js for syntax highlighting which can conflict with Hugo. I had to disable Highlight.js and enable Chroma in my hugo configuration.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# hugo.yaml
params:
  assets:
    disableHLJS: true

markup:
  highlight:
    codeFences: true
    guessSyntax: true
    lineNos: true
    noClasses: false
    style: github

The noClasses: false tells Hugo to use inline styles instead of CSS classes. The color is written directly on every single token, with no external CSS needed. <span style="color:#cf222e">func</span> <span style="color:#1f2328">printGreeting</span>

Generating themes

You can generate CSS stylesheet for the Chroma CSS classes using Hugo CLI.

1
hugo gen chromastyles --style=monokailight > themes/PaperMod/assets/css/includes/chroma-styles.css

Reference:

Download themes (deprecated)

Copy extended_head.html from the theme folder to ./layout/ folder. Get highlight.js from the CDN. Paste to extended_head.html.

Reference:

Syntax Highlight with Light/Dark mode (Hugo 0.164+)

The new Hugo (0.166+) supports light/dark mode for syntax highlight.

Run the following to create the theme file for light mode:

  • hugo gen chromastyles --style=monokai --mode=light > assets/css/highlight-light.css
  • hugo gen chromastyles --style=github --mode=light > assets/css/highlight-light.css

And dark mode:

  • hugo gen chromastyles --style=monokai --mode=dark --modeSelector > assets/css/highlight-dark.css
  • hugo gen chromastyles --style=github-dark --mode=dark --modeSelector > assets/css/highlight-dark.css

Note that not all themes support light/dark modes.

PaperMod theme changeswhen you toggle the theme icon. That is why highlight-dark.css contains .dark prefixed, so it gets enabled when html tags has the dark class.

Reference:

  • https://gohugo.io/quick-reference/syntax-highlighting-styles/
  • https://gohugo.io/configuration/markup/#highlight

Inline code vs Block code

Hugo’s built-in Chroma syntax highlighter does not natively highlight standard inline code wrapped in single backticks. Chroma is designed to process fenced code blocks (multi-line blocks using triple backticks) or the highlight shortcode. Fenced blocks (three backticks with a language) go through Chroma and get the chroma, line, cl classes

Single-backtick inline code like foo renders as a plain <code>foo</code>, with no Chroma classes. The stylesheet has nothing to match, so the colors never apply, in light or dark mode.

Adding Google Analytics

To add analytics to your Hugo website, assuming your current theme doesn’t support it automatically by just updating the settings in hugo.yaml, do the following:

  • Go to analytics.google.com, crete your account, and copy the code provided by Google.
  • Create a new partial file called analytics in hugo_root/layouts/_partials/google/analytics.html
  • Paste the code there, save, close
  • Include the partial in your theme. Copy the original theme file from themes/PaperMod/layouts/partials/head.html to ./layout/_partials/head.html, then modify the later to include the created partial using {{ - partial "google/analytics.html" . -}}.

Adding TeX Support

Check out the post about this at Huxo TeX Support.

Which Hugo Version

To get your hugo version just ran hugo version.

To update, if you used brew, just run brew update hugo. It will update hugo, for example hugo 0.147.4 -> 0.166.0.