HUGO
News Docs Themes Community GitHub

New template system in Hugo v0.146.0

Overview of the new template system in Hugo v0.146.0.

In Hugo v0.146.0, we performed a full re-implementation of how Go templates are handled in Hugo. This includes structural changes to the layouts folder and a new, more powerful template lookup system.

We have aimed to maintain as much backward compatibility as possible by mapping “old to new,” but some reported breakages have occurred. We’re working on a full overhaul of the documentation on this topic – until then, this is a one-pager with the most important changes.

Changes to the layouts folder

DescriptionAction required
The _default folder is removed.Move all files in layouts/_default up to the layouts/ root.
The layouts/partials folder is renamed to layouts/_partials.Rename the folder.
The layouts/shortcodes folder is renamed to layouts/_shortcodes.Rename the folder.
Any folder in layouts that does not start with _ represents the root of a Page path. In Hugo v0.146.0, this can be nested as deeply as needed, and _shortcodes and _markup folders can be placed at any level in the tree.No action required.
The above also means that there’s no top-level layouts/taxonomy or layouts/section folders anymore, unless it represents a Page path.Move them up to layouts/ with one of the Page kinds section, taxonomy or term as the base name, or place the layouts into the taxonomy Page path.
A template named taxonomy.html used to be a candidate for both Page kind term and taxonomy, now it’s only considered for taxonomy.Create both taxonomy.html and term.html or create a more general layout, e.g. list.html.
For base templates (e.g., baseof.html), in previous Hugo versions, you could prepend one identifier (layout, type, or kind) with a hyphen in front of the baseof keyword.Move that identifier after the first “dot,” e.g., renamelist-baseof.html to baseof.list.html.
We have added a new all “catch-all” layout. This means that if you have, e.g., layouts/all.html and that is the only template, that layout will be used for all HTML page rendering.
We have removed the concept of _internal Hugo templates.1Replace constructs similar to {{ template "_internal/opengraph.html" . }} with {{ partial "opengraph.html" . }}.
The identifiers that can be used in a template filename are one of the Page kinds (home, page, section, taxonomy, or term), one of the standard layouts (list, single, or all), a custom layout (as defined in the layout front matter field), a language (e.g., en), an output format (e.g., html, rss), and a suffix representing the media type. E.g., all.en.html and home.rss.xml.
The above means that there’s no such thing as an index.html template for the home page anymore.Rename index.html to home.html.

Also, see the Example folder structure below for a more concrete example of the new layout system.

Changes to template lookup order

We have consolidated the template lookup so it works the same across all shortcodes, render hooks, partials, and page templates. The previous setup was very hard to understand and had a massive number of variants. The new setup aims to feel natural with few surprises.

The identifiers used in the template weighting, in order of importance, are:

IdentifierDescription
Layout customThe custom layout set in front matter.
Page kindsOne of home, section, taxonomy, term, page.
Layouts standard 1list or single.
Output formatThe output format (e.g., html, rss).
Layouts standard 2all.
LanguageThe language (e.g., en).
Media typeThe media type (e.g., text/html).
Page pathThe page path (e.g., /blog/mypost).
Typetype set in front matter.2

For templates placed in a layouts folder partly or completely matching a Page path, a closer match upwards will be considered better. In the Example folder structure below, this means that:

  • layouts/docs/api/_markup/render-link.html will be used to render links from the Page path /docs/api and below.
  • layouts/docs/baseof.html will be used as the base template for the Page path /docs and below.
  • layouts/tags/term.html will be used for all term rendering in the tags taxonomy, except for the blue term, which will use layouts/tags/blue/list.html.

Example folder structure

layouts
├── baseof.html
├── baseof.term.html
├── home.html
├── list.html
├── single.html
├── taxonomy.html
├── term.html
├── term.mylayout.en.rss.xml
├── _markup
│   ├── render-codeblock-go.term.mylayout.no.rss.xml
│   └── render-link.html
├── _partials
│   └── mypartial.html
├── _shortcodes
│   ├── myshortcode.html
│   └── myshortcode.section.mylayout.en.rss.xml
├── docs
│   ├── baseof.html
│   ├── _shortcodes
│   │   └── myshortcode.html
│   └── api
│       ├── mylayout.html
│       ├── single.html
│       └── _markup
│           └── render-link.html
└── tags
    ├── taxonomy.html
    ├── term.html
    └── blue
        └── list.html

  1. The old way of doing it made it very hard/impossible to, e.g., override _internal/disqus.html in a theme. Now you can just create a partial with the same name. ↩︎

  2. The type set in front matter will effectively replace the section folder in Page path when doing lookups. ↩︎