Hugo: Leaf and Branch Bundles
— Kaushal ModiContent organization for Hugo posts/articles using Page Bundles.
- Fix the
term
andtaxonomy
page Kind references updated as of Hugo v0.73.0. - Use the Hugo v0.53 introduced
site
var in examples. - Clarify the leaf vs branch decision making section.
Hugo v0.32 introduced a new feature called Page Bundles (1, 2), as a way to organize the content files. It is useful for cases where a page or section’s content needs to be split into multiple content pages for convenience, or has associated attachments like documents or images.
A Page Bundle can be one of two types:
- Leaf 🍃 Bundle
- Branch 🌿 Bundle
The leaf and branch nodes from Data Structure Trees serve as a great analogy to help relate with the Hugo bundles.
- Leaf
- A node with no children.
- Branch
- A node with at least one child.
Quick Summary #
Here’s a quick summary of the differences between these bundle types — Details follow in the sections after.
Leaf Bundle | Branch Bundle | |
---|---|---|
Usage | Collection of content and attachments for single pages | Collection of attachments for section pages (home page, sections, taxonomy list pages, term list pages) |
Index file name | index.md 1 | _index.md 1 |
Allowed Resources | Page and non-page (like images, pdf, etc) types | Only non-page (like images, pdf, etc.) types |
Where can the Resources live? | At any directory level within the leaf bundle directory. | Only in the directory level of the branch bundle directory i.e. the directory containing the _index.md (ref). |
Layout type | single | list |
Nesting | Does not allow nesting of more bundles under it | Allows nesting of leaf or branch bundles under it |
Example | content/posts/my-post/index.md | content/posts/_index.md |
Content from non-index page files .. | Accessed only as page resources | Accessed only as regular pages |
Hugo v0.73.0 made a much needed fix — It finally renamed taxonomy
Page Kind to term
, and taxonomyTerm
to taxonomy
. Now if you have
a tags = ["abc"]
front-matter, tags is the taxonomy and abc is
a term.
Leaf Bundle #
A Leaf Bundle is a directory at any hierarchy within the content/
directory, that contains an index.md
file.
Examples of Leaf Bundle organization #
content/
├── about
│ └── index.md
├── posts
│ ├── my-post
│ │ ├── content1.md
│ │ ├── content2.md
│ │ ├── image1.jpg
│ │ ├── image2.png
│ │ └── index.md
│ └── my-another-post
│ └── index.md
└── another-section
├── ..
└── not-a-leaf-bundle
├── ..
└── another-leaf-bundle
└── index.md
In the above example content/
directory, there are four leaf
bundles:
- about
- This leaf bundle is at the root level (directly under
content
directory) and has only theindex.md
. - my-post
- This leaf bundle has the
index.md
, two other content Markdown files and two image files. The content from the two non-index.md
files can be accessed only as page resources, not as regular files i.e. those non-index.md
files will not have their own permalinks or HTML files inpublic/
. - my-another-post
- This leaf bundle has only the
index.md
. - another-leaf-bundle
- This leaf bundle is nested under couple of
directories. This bundle also has only the
index.md
.
The hierarchy depth at which a leaf bundle is created does not matter, as long as it is not inside another leaf bundle.
Headless Bundle #
A headless bundle is a bundle that is configured to not get published anywhere:
- It will have no
Permalink
and no rendered HTML inpublic/
. - It will not be part of
site.RegularPages
, etc.
But you can get it by site.GetPage
. Here is an example:
{{ $headless := site.GetPage "page" "some-headless-bundle" }}
{{ $reusablePages := $headless.Resources.Match "author*" }}
<h2>Authors</h2>
{{ range $reusablePages }}
<h3>{{ .Title }}</h3>
{{ .Content }}
{{ end }}
A leaf bundle can be made headless by adding below in the Front Matter
(in the index.md
):
headless = true
Only leaf bundles can be made headless.
Example uses of headless page bundles:
- Shared media galleries
- Reusable page content “snippets”
Branch Bundle #
A Branch Bundle is a directory at any hierarchy within the
content/
directory, that contains an _index.md
file. This
_index.md
can also be directly under the content/
directory, to
set the Home page content and/or front-matter variables.
By default, Hugo considers the first directory level under
content/
as a section. But a branch bundle can be used to create a
section at any hierarchy.
Examples of Branch Bundle organization #
content/
├── _index.md
├── branch-bundle-2
│ ├── branch-content1.md
│ ├── branch-content2.md
│ ├── image1.jpg
│ ├── image2.png
│ └── _index.md
└── branch-bundle-3
├── _index.md
└── a-leaf-bundle
└── index.md
In the above example content/
directory, there are three branch
bundles (and a leaf bundle):
- Home page as branch bundle
- The home page content is organized as a
branch bundle, using
_index.md
directly in thecontent/
directory. It consists of two other branch bundles. branch-bundle-2
- This branch bundle has the
_index.md
, two other content Markdown files and two image files. The content from the two non-index.md
files can be accessed only as regular pages, not as page resources. Branch bundles cannot have page resources, so any non-index Hugo recognized content file in a branch bundle can be accessed like any regular page (just like in any section.. as.. a branch bundle is a section too). branch-bundle-3
- This branch bundle has the
_index.md
and a nested leaf bundle.
The hierarchy depth at which a branch bundle is created does not matter.
Branch Bundle vs Regular Sections #
- Branch Bundle section
- Any directory in
content/
that contains the_index.md
file. - Regular Section
- Any first-level directory in
content/
, and also home page andtaxonomy
list page .. that is not already a Branch Bundle.
Branch Bundle Section | Regular Section | |
---|---|---|
.Kind | "section" | "section" |
.File.Path | /path/to/_index.md | "" |
Layout type | list | list |
Can contain non-page Resources? | Yes | No |
Can contain page Resources? | No | No |
Branch Bundle is basically a Section with non-page Resources.
Here’s an example snippet that can be used in a list
template to
distinguish between the two:
{{ if (eq .Kind "section") }} <!-- Section -->
{{ if (eq .File.Path "") }} <!-- Regular section -->
<h1>Posts in ‘{{ .Dir | default .Section }}’</h1>
{{ else }} <!-- Branch bundle section -->
<h1>{{ .Title }}</h1>
<h2>Posts in ‘{{ .Dir | default .Section }}’</h2>
{{ end }}
{{ end }}
So, which bundle should I use? #
In summary:
If it’s a regular page or a single page where you write your content yourself, create a leaf bundle.
Page Bundles of
page
Kind are always leaf bundles.. and vice versa.Otherwise, if it’s a list page which Hugo generates for you, create a branch bundle. Such pages typically are a list of regular pages or even other list pages. Pages of
home
,section
,taxonomy
andterm
Kind are always branch bundles.
Upgrading to Hugo v0.32? #
All of this boils down to these few caution points if you are upgrading Hugo from a pre-0.32 version to 0.32 or a newer version:
- If a directory
foo
has anindex.md
(leaf bundle), that file will be the content file for thatfoo
Regular Page. - If a directory
foo
has an_index.md
(branch bundle), that file will be the content file for thatfoo
Section Page, and all other2.md
files in the same or deeper hierarchy levels underfoo/
will become Regular Pages under thatfoo
section.
Examples #
Of course, a post like this is not complete without examples, so here they are:
Example | Markdown source | HTML output |
---|---|---|
This page (leaf bundle) | content/posts/hugo-leaf-and-branch-bundles/ | duh! |
Leaf and branch bundle examples from ox-hugo test site | content/bundles/ | Bundles |
Headless (leaf) bundle example from the same | content page using headless bundle + Layout fetching headless bundle pages | Page using the Headless Page Bundle |
Branch bundles used for home , taxonomy , and term Kind pages | Hugo Sandbox content/ dir | Hugo Sandbox |
Further Reading #
I suggest reading the below pages in the Hugo documentation, as they complement the Page Bundles concept:
The
.md
extension forindex.md
,_index.md
, and all other content files in this post is just an example. The extension can be.html
or any of any valid MIME type recognized by Hugo. ↩︎ ↩︎It’s a qualified “all other” — That does not count the content files further nested in leaf and branch bundles in that
foo
section. ↩︎