Hugo Input Formats, Directory Layout, and Workflow

The Hugo static site generator takes some plain-text content, marries it to a bunch of HTML templates, and produces a set of complete, static HTML pages that can be served by any generic, stand-alone web server. It allows for a variety of input formats, and expects a particular layout of its workspace.

Part 2: Input Formats, Directory Layout, Tooling and Workflow

In its most basic mode of operation, Hugo expects a hierarchy of input files in a source directory (by default called content/). When run, Hugo marries these to the appropriate templates and places the generated HTML files in an output directory (by default: public/). The content of this directory can be placed, as is, in the docroot of a public-facing web server. If Hugo encounters other files (such as images or stylesheets) in the source directory, it copies them unmodified to the output directory.

Input Formats

Input format for “content” is usually Markdown. Out of the box, Hugo can also handle plain HTML and Emacs Org-Mode. AsciiDoc, reStructuredText, and Pandoc require the appropriate tools to be installed. Hugo determines the format by the file extension (.md, .html, .org) or from the frontmatter or preamble of each file.

Frontmatter

Hugo input files typically begin with a preamble in YAML format. (TOML and JSON are also possible.) The preamble can be used to set configuration values for each piece of content: for example, it is possible to assign keyword tags to the page, to modify its public URL, or to specify the specific template to render it.

Here is an example (in YAML format):

---
title: "A Hugo Survival Guide"
date: "2020-02-22T22:22:22-00:00"
slug: "a-hugo-survival-guide"
---

Notice the YAML-specific fencing with ---. (TOML uses +++, and for JSON, the entire preamble needs to be enclosed in curlies {...}.)

Input File Warnings and Remarks

Frontmatter parameters can interfere with rendering in surreptitious ways. For example, by default, Hugo creates new content items with a frontmatter parameter draft set to true, with the consequence that no output will be generated for this piece of content.

Markdown must be HTML-compatible. For example, it is not possible to have emphasis (_ ... _) span multiple paragraphs. If you want to emphasize two consecutive paragraphs, emphasize them individually.

There may be additional problems when using JavaScript (such as MathJax) on the created website. Any JavaScript library runs after template expansion is complete; it may therefore be necessary to protect some markup from being interpreted as Markdown. (Underscores, specifically, are both used by Markdown and MathJax, but for different purposes: emphasis or italics here, subscripts there.)

Markdown is less expressive than HTML. In particular, Markdown has no provisions to include rich formatting directives (such as color specifications or font changes) in a Markdown file. Embedded HTML or shortcodes provide workarounds. (Shortcodes are pre-defined snippets of Go Template language that can be embedded in a Markdown file and will be evaluated when the content is rendered.) Note that by default, recent versions of Hugo do not pass through embedded HTML, but discard it. (See next paragraph.)

HTML files without frontmatter, in general, will be copied to the output as-is, whereas HTML files with frontmatter (even if it is empty) will be treated as content files and subject to template expansion.

Usually, HTML that is embedded in Markdown is passed through untouched. Recent versions of Hugo do not behave like this by default; instead, the following addition must be made to the global configuration file config.toml to enable this behavior:

[markup]
  [markup.goldmark]
    [markup.goldmark.renderer]
      unsafe = true

Input processors are often external tools or libraries that Hugo uses to parse and process input files; some may behave slightly different than others. The Hugo documentation may not always be up-to-date in this regard.

Important Frontmatter Parameters

Hugo defines a large number of parameters that can be set via the frontmatter; themes may define additional ones. In general, these parameters are optional: they are a low-level way to override global configurations (either default or from the global configuration file) for a specific piece of content. Two remarks:

  • There is no magic here. All that the “frontmatter” does is to populate or override some of Hugo’s internal key/value data structures.

  • Frontmatter is nice because it often gives fairly detailed control over individual pieces of content. At the same time, it scatters configuration information widely. I have found it better to rely as much as possible on global configurations and only override them, where necessary, for individual pieces of content.

Some of the more important pre-defined parameters include the following:

Metadata
date
The primary “date” associated with this page. Other date-related frontmatter variables are expiryDate, publishDate, and lastmod. (If present, Hugo will use the first two to decide whether to publish this piece of content or not.)
description, keywords, author
May be used by the template to populate <meta> tags in the HTML header. (Not all themes use this information.)
tags, categories
Keyword tags assigned to this piece of content. Hugo uses this information to create lists of all tagged pages. (Also see the discussion of “Taxonomies”.)
Naming and URL Management
title
This is typically used by the template to populate the page’s headline and to set the page’s title in the HTML header. It may be required by some themes for the site to work properly
slug
Specify the slug (that is, the last part of the URL); if not present, the slug will be generated from the file name.
url
Complete path, relative to the document root, for this piece of content. Intermediate directories will be created as necessary.
aliases
One or more paths, relative to the document root. For each one, Hugo will create a file with a redirect notice that redirects to the current piece of content. (Note the spelling of the keyword!)
Processing instructions
draft
Do not publish, unless the -D or --buildDrafts option is given. (Hugo sets this parameter to true by default. It is absolutely safe to remove this parameter from the frontmatter of any piece of content.)
layout
The template to be used to render this document. (The details of the template lookup are complicated, see the Hugo documentation.)
weight
A numeric value that is used when sorting pages (in list views, for instance). May be positive or negative, integer or floating point.
markup
Specify the markup format of the current document.

Directory Layout

Hugo assumes a specific layout of its working directory. You can use Hugo itself to create a skeleton workspace directory for a new project. Run:

hugo new site demo

This will create a new directory called demo. In this directory, you will find the following files and directories:

config.toml     site-wide configuration file
archetypes/     skeleton documents, frontmatter (see below)
content/        plain-text content (the input directory)
data/
layouts/        template files
static/         static docs, like images or JavaScript files
themes/

All of them are empty or almost empty by default.

All content, in plain text (Markdown), goes into the content/ directory; this is the source or input directory. Notice that while frontmatter is typically YAML, the global config file is usually TOML.

The archetype/ directory holds skeleton outlines for content documents; essentially some default frontmatter. They are used by the hugo new command to create new content files. Archetypes may contain references to Hugo variables; if so, they are be evaluated by hugo new. The frontmatter in the resulting starter files will contain only values, not references to Hugo variables.

Complete, downloaded themes go into the themes/ directory. It is possible to provide customized templates that may override some of the theme’s settings; they go into the layouts/ directory.

The static/ directory is a repository for static files that are not rendered via templates, such as images, stylesheets, or JavaScript libraries. It is possible to create subdirectories (such as static/imgs/); they will be replicated into the output directory. The data/ directory, finally, is intended as a place for additional configuration data, or for static data to populate a page.

Hugo recognizes additional directories. For example, if one wants to let Hugo process a stylesheet, then it needs to be placed into an assets/ directory. These directories are not created by default; see the Hugo documentation for additional details.

Finally, many of these directories can be changed using command-line flags. Keep in mind, however, that Hugo will generally only access files that are below the current working directory; it will not follow arbitrary paths.

Tooling

The workflow description below assumes that you already have Hugo installed. As a Go program, Hugo is a stand-alone application that does not depend on external libraries. There are two different versions of Hugo in a release. The “extended” version includes some additional functionality for image processing (scaling and cropping), for the compilation of SASS/SCSS files, and some other features. Be aware that some themes require the extended version!

In general, the hugo command must be executed within the the project’s workspace directory. The hugo command takes a number of subcommands and command-line options. Some of the most important subcommands are:

hugo
When invoked without a subcommand, Hugo will process the inputs and static files, creating a set of static HTML pages and auxiliary files. By default, Hugo will create a directory public/ inside the project’s workspace as destination for the output files. Hugo also creates a resources/ directory as destination for intermediate results (such as cropped images).
hugo server
When invoked with the server command, Hugo does not write its results out to disk. Instead, it starts up an HTTP server (by default on port 1313) and serves the generated pages from memory. The server watches the content directory and configuration file, and automatically refreshes the results when it detects changes. (This is primarily intended as a development tool.)
hugo new
The new command, when followed by a path in the content/ directory, will create a new file (as well as any required intermediate directories) at the given location. The file will be empty, except for the frontmatter, which will have been pulled from the archtetypes/ directory. Any Hugo variables within the frontmatter will have been evaluated.
hugo new site
This, when followed by the desired directory name, will create a new workspace directory of the given name, and containing the default workspace directory hierarchy.

Compared to other build systems, Hugo’s tooling is fairly rudimentary. Many activities (such as adding a theme) require multiple, manual steps.

There is no clean command or target. If there already is a public/ output directory, Hugo will continue placing results into it. It will be necessary to manually remove public/ (and resources/) to get a fresh start.

Diagnostics are extremely poor. If something doesn’t work, it is very difficult to get any information what might be causing the problem. (At the same time, the hugo command does not follow the Unix tradition to “succeed silently, but fail loudly”: it will chattily and unnecessarily tell you when it has created a new file using hugo new, but it will silently discard various input files according to its own processing rules, leaving you none the wiser.)

Finally, the hugo command (in particular the server) can get easily confused or even crash when dealing with malformed input files, or the temporary files that editors sometimes leave in working directories.

Workflow

Here are the steps to create a project, add a theme, and begin creating content. Remember that, out of the box, Hugo does not contain a default theme: hence, not only is it necessary to add a theme, but also to configure it.

Note that this sequence of steps contains additional steps, compared to the version in the Hugo documentation!

  1. Create a workspace and skeleton directory hierarchy using hugo new site

  2. Add a theme into the themes directory in the workspace. This can be done using git or by downloading a theme and copying it to its destination; it is not done using a hugo subcommand.

  3. A theme typically contains an exampleSite directory, containing a complete website demonstrating the theme. Use its global configuration file (typically: exampleSite/config.toml) as basis for the configuration file of your new project. Edit your copy of this file as needed. (The reason is that many themes require specific configurations values to work properly; using the configurations from the example site as basis provides a fairly reliable starting point.)

  4. Possibly copy the contents of the theme’s archetypes/ directory into the corresponding top level location. (Again, the reason is that some themes require specific frontmatter variables that the default archetypes will not know about.)

  5. Create some pieces of content using hugo new. Edit them as desired. (Remember to remove any draft: true lines from the frontmatter, otherwise Hugo will ignore the piece of content.) Alternatively, copy the files from the example site into your content/ directory to have a starting point.

  6. Start the development server using hugo server. (Default port 1313, use the -p flag to change the port.)

  7. Examine the site at http://localhost:1313. Hugo injects some JavaScript into the generated HTML which reloads the page automatically whenever the content or configuration of the site changes. This is convenient, but I found it occasionally a bit flaky if many changes are made quickly, or in the presence of symbolic links.

  8. Finally, build the site using hugo. Deploy the contents of the public/ output directory to the hosting provider of your choice. Remember that Hugo does not clean an existing output directory automatically if there is one.