Hugo Page Bundles: Organize Assets, Optimize Images, and Avoid Broken Paths
Hugo page bundles scope images and assets to content directories. Learn how leaf, branch, and headless bundles enable image processing and avoid broken paths.
23 Mar 2026, 07:10 UTC

The problem: scattered assets and missed optimization
Hugo sites often end up with images in static/, duplicated across posts, or referenced with fragile relative paths. The built‑in image pipeline (Resize, Fill, Convert) only processes page resources—files that live inside a page bundle. If you drop an image in static/images/ and reference it from Markdown, Hugo copies it verbatim; no WebP conversion, no responsive variants, no cache‑busting fingerprint.
Page bundles turn a directory into a self‑contained unit: content, resources, and URL all travel together. Understanding the three bundle types—leaf, branch, and headless—lets you keep assets close to the content that uses them, share assets across a section, or expose a global library without creating stray pages.
Leaf bundles: one page, its own resources
A leaf bundle is a directory containing index.md plus any images, CSS, or JS that belong to that single page. The directory name becomes the page’s URL (.RelPermalink). Resources are page‑relative: {{< figure src="hero.jpg" >}} resolves to hero.jpg inside the same folder, and Hugo’s image pipeline can transform it.
- Use for blog posts, documentation pages, any content that owns its assets.
- Moving
index.mdout of the bundle changes the URL and breaks resource references unless you update them.
Branch bundles: section lists and shared assets
A branch bundle uses _index.md instead of index.md. It renders a list page (e.g., /posts/) and can have child pages in subdirectories. The branch bundle’s own resources are not directly addressable by URL, but descendant pages can pull them with .Resources.Match. This enables a shared asset library for a whole section—think a common diagram style or a set of icons used by every post in content/posts/.
- Branch bundles drive taxonomy term pages and section templates (
list.html,single.html). - They do not render their own
.Contentunless a layout explicitly prints it.
Headless bundles: global libraries without pages
Add headless: true to a bundle’s front matter (works on leaf or branch). Hugo publishes the resources but creates no page in .Site.RegularPages. You access them via .Site.GetPage or, when mounted through a Hugo Module, via .Resources.Match from any page. Ideal for design‑system assets, shared JavaScript, or a theme’s image components.
Worked example: leaf bundle with image processing
Assume you have the extended Hugo binary (required for image transforms). In your project root:
mkdir -p content/posts/my-post
cat > content/posts/my-post/index.md <<'EOF'
---
title: "My Post"
date: 2026-01-15
---
{{< figure src="hero.jpg" alt="Hero image" >}}
EOF
# Place an image in the bundle
cp /path/to/hero.jpg content/posts/my-post/hero.jpg
Run the development server:
hugo server --disableFastRender
Open http://localhost:1313/posts/my-post/. Inspect the rendered <img> tag; the src should point to a fingerprinted WebP like /posts/my-post/hero_hu123456.webp. In public/posts/my-post/ you’ll see the processed file alongside the original. If you use the standard (non‑extended) Hugo binary, the shortcode still works but the image is copied unchanged—no error, no warning.
Trade‑offs and limitations
| Constraint | Impact |
|---|---|
| Extended binary required | Standard build silently skips transforms; CI must install hugo_extended. |
| Case‑sensitive matching | Linux treats Hero.jpg ≠ hero.jpg; macOS/Windows do not. Enforce lowercase filenames in the repo. |
| No nested bundles | A bundle cannot contain another index.md or _index.md; only one level of page resources per page. |
| Remote content not storable | getJSON/getCSV fetch at build time; results cannot be saved as page resources. |
| Module mount baseURL | Absolute resource URLs in front matter break when a module is mounted under a subpath. |
Verify your setup
- Build with
hugo --minifyand checkpublic/for fingerprinted assets (e.g.,hero_hu123456.webp). - Confirm headless bundles appear in
.Site.GetPagebut not in.Site.RegularPages. - If using modules, run
hugo mod graphand verify mounts withhugo config mounts.
Start small: convert one existing static/ image into a leaf bundle, add a figure shortcode, and watch the pipeline produce optimized output. The structure pays off as the site grows—assets stay with their content, URLs stay stable, and the image pipeline just works.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.