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
, andlastmod
. (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 totrue
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 aresources/
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 thecontent/
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 thearchtetypes/
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!
-
Create a workspace and skeleton directory hierarchy using
hugo new site
-
Add a theme into the
themes
directory in the workspace. This can be done usinggit
or by downloading a theme and copying it to its destination; it is not done using ahugo
subcommand. -
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.) -
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.) -
Create some pieces of content using
hugo new
. Edit them as desired. (Remember to remove anydraft: true
lines from the frontmatter, otherwise Hugo will ignore the piece of content.) Alternatively, copy the files from the example site into yourcontent/
directory to have a starting point. -
Start the development server using
hugo server
. (Default port 1313, use the-p
flag to change the port.) -
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. -
Finally, build the site using
hugo
. Deploy the contents of thepublic/
output directory to the hosting provider of your choice. Remember that Hugo does not clean an existing output directory automatically if there is one.