9.4 Directory Archetypes: Bundled Content with Resources
Alright, let’s talk about Directory Archetypes. This is where Hugo stops being a simple static site generator and starts feeling like a proper, grown-up content management system. The idea is deliciously simple: stop thinking in terms of individual files and start thinking in terms of bundles of content and resources.
Think of it like this. You have a “thing” on your site—let’s say a project, or a product, or a person. That “thing” isn’t just a blob of text; it’s a title, a description, some body copy, and it has assets: a hero image, a PDF spec sheet, a gallery of screenshots, maybe even a custom CSS or JavaScript file. In a traditional setup, you’d have your project.md in one folder and your images/project/ in another, and keeping them in sync is a manual, error-prone nightmare.
Hugo’s directory archetypes solve this by letting you put all of that—the content file and all its associated resources—into a single, self-contained folder. This isn’t just neat and tidy; it’s a paradigm shift. It makes your content portable, easy to reason about, and a dream to back up or version control.
Leaf Bundles: The Self-Contained Powerhouse
A Leaf Bundle is a folder that represents a single piece of content. Its name is defined by the presence of an index.md or _index.md file inside it. Everything else in that folder is its dedicated resource.
Here’s the file structure of a typical Leaf Bundle for a project:
content/
└── projects/
└── my-awesome-app/ # This is the Leaf Bundle directory
├── index.md # The front matter and content for the project
├── hero-image.jpg # A page resource
├── screenshot-1.png # Another page resource
├── spec-sheet.pdf # Yep, even PDFs!
└── demo/
└── index.html # This is ALSO a page resource!
The magic is in how you access these resources from your template. You don’t fumble with convoluted relative paths. Hugo gives you a .Resources object on the page.
{{/* layouts/projects/single.html */}}
<article>
<h1>{{ .Title }}</h1>
<img src="{{ (.Resources.Get "hero-image.jpg").Permalink }}" alt="{{ .Title }}">
<div>{{ .Content }}</div>
<h2>Screenshots</h2>
<div class="gallery">
{{ range where .Resources "Name" "like" "screenshot-" }}
<img src="{{ .Permalink }}" alt="Screenshot">
{{ end }}
</div>
<p><a href="{{ (.Resources.Get "spec-sheet.pdf").Permalink }}">Download the Spec Sheet (PDF)</a></p>
</article>
Why is this brilliant? The path always works. Move the entire my-awesome-app folder somewhere else? The links still work. It’s all relative to the bundle itself. This is the kind of feature that makes you want to hug the Hugo developers. Well, most of them.
Branch Bundles: The Index-First Collection
If a Leaf Bundle is for a single page, a Branch Bundle is for a section that has its own content and contains other pages (which can be Leaf Bundles or markdown files). The key identifier is an _index.md file (note the underscore). This file defines the front matter and content for the section itself.
content/
└── projects/ # This is a Branch Bundle directory
├── _index.md # Content for the /projects/ page itself
├── featured-image.jpg # A section resource!
├── my-awesome-app/ # A Leaf Bundle nested inside
│ └── index.md
└── another-project.md # A regular page file
You access the section’s resources in a similar way, but from the section page template.
{{/* layouts/projects/list.html */}}
<section>
<h1>{{ .Title }}</h1>
<img src="{{ (.Resources.Get "featured-image.jpg").Permalink }}" alt="">
<p>{{ .Content }}</p>
{{/* ... list child pages ... */}}
</section>
The Gotchas: Where the Brilliance Gets a Little Dimmable
It’s not all rainbows and resource bundles. There are quirks.
First, the naming convention is a bit… precious. index.md for a Leaf Bundle, _index.md for a Branch Bundle. Screw this up and Hugo will either throw a fit or, worse, silently do the wrong thing. It feels like a holdover from a more opinionated time, and while I get the logic, it’s a common foot-gun.
Second, performance. If you have a page with 500 high-res images, using .Resources.Get in a loop will build that page every single time any of those images change. For massive bundles, this can slow down your local development server to a crawl. The best practice here is to be judicious. For huge, image-heavy sections (like a photo gallery), consider using the assets/ directory (global resources) instead and managing them with pipes, or using a CDN. The bundle pattern is best for a manageable number of assets per page.
Finally, remember that these resources are page- or section-specific. You can’t easily grab an image from one Leaf Bundle to use in another without some funky Hugo magic (looking at you, resources.GetRemote). This is by design—it enforces encapsulation—but it can be frustrating if you’re used to a global media library. Plan your content structure accordingly. This isn’t WordPress; it’s a feature, not a bug, but it’s one you need to architect for.