Organizing Hugo Sites with Content Bundles: A Practical Guide
Hugo content bundles let you co-locate content with its assets and create hierarchical sections without extra configuration. This guide covers branch vs. leaf bundles, a worked documentation example, permalink gotchas, and when to adopt (or avoid) the pattern.
29 Sept 2025, 00:51 UTC

The Problem: Scattered Content Files
As a Hugo site grows, content organization becomes a real headache. You start with a flat content/posts/ directory, but soon you have documentation, tutorials, case studies, and blog posts all mixed together. URL structures get messy, templates become conditional spaghetti, and finding related assets (images, PDFs, code samples) for a specific piece of content turns into a treasure hunt.
Hugo's content bundles solve this by letting you group a piece of content with its assets in a single directory. Instead of content/posts/my-post.md and static/images/my-post-diagram.png living in separate trees, they live together. This post walks through when bundles help, how to set them up, and where they add friction.
What Is a Content Bundle?
A content bundle is a directory that Hugo treats as a single content unit. The directory contains an _index.md file (the bundle's primary content) and any number of supporting files—images, data files, PDFs, even other Markdown files that become child pages. Hugo 0.80 stabilized this behavior; earlier versions may not render _index.md correctly.
There are two bundle types:
- Leaf bundles: A directory with
index.md(no underscore). The directory becomes a single page; other files in it are page resources accessible via.Resourcesin templates. - Branch bundles: A directory with
_index.md. The_index.mdrenders as a list page showing child pages, and each child file (e.g.,page1.md,page2.md) renders as its own single page.
Branch bundles are the ones that enable hierarchical taxonomies and section-like behavior without needing a separate _index.md at the section root.
Worked Example: Documentation Section as a Branch Bundle
Suppose you're adding a documentation section to a site. You want /docs/ to list all guides, each guide at /docs/guide-name/, and each guide's screenshots co-located with its content.
Run these commands from your Hugo site root (no elevated permissions needed):
mkdir -p content/docs/getting-started content/docs/advanced-config
cat > content/docs/_index.md << 'EOF'
---
title: "Documentation"
weight: 10
---
Welcome to the documentation. Browse guides below.
EOF
cat > content/docs/getting-started/index.md << 'EOF'
---
title: "Getting Started"
weight: 1
---
First steps for new users.

EOF
# Add a diagram asset (replace with a real image file)
touch content/docs/getting-started/architecture.png
cat > content/docs/advanced-config/index.md << 'EOF'
---
title: "Advanced Configuration"
weight: 2
---
Deep-dive into config options.
EOF
Start the dev server:
hugo server -D
Visit http://localhost:1313/docs/. You should see a list page rendering _index.md with links to /docs/getting-started/ and /docs/advanced-config/. Each child page renders its own content and can reference architecture.png via .Resources.GetMatch in templates.
Verification: Check that hugo server output shows the pages being created (look for content/docs/getting-started/index.md and content/docs/advanced-config/index.md in the built page list). If /docs/ returns 404, confirm your Hugo version is ≥ 0.80 (hugo version).
Trade-offs and Limitations
Bundles add a directory level to every content piece. A flat content/posts/post-name.md becomes content/posts/post-name/index.md. This affects:
- Permalinks: Default permalink patterns like
posts/:slug/may produce/posts/post-name/(desired) or/posts/post-name/index/(undesired) depending on configuration. Setuglyurls = falseinhugo.tomland usepermalinks: posts: "/:slug/"to keep URLs clean. - Editor workflows: Some CMS tools and static site editors expect flat files. You may need to configure them to recognize bundle directories.
- Git diffs: Moving existing content into bundles creates noisy diffs (many file renames). Do it in a dedicated commit.
Branch bundles also mean _index.md is a list page, not a standalone article. If you want a landing page and child pages, that's exactly what you get. If you want a single page with resources, use a leaf bundle (index.md without underscore).
When to Use Bundles (and When Not To)
Use branch bundles for: documentation sections, tutorial series, multi-part guides, any content group that benefits from a shared list page and co-located assets.
Use leaf bundles for: individual blog posts with custom images, portfolio items with galleries, any page where assets belong to that page alone.
Stick with flat files for: simple blogs with no per-post assets, sites where URL structure is locked down and changing it breaks external links, teams not ready for the directory-per-post convention.
Next Steps
Try converting one existing section to a branch bundle. Pick a low-traffic area (e.g., content/case-studies/), create _index.md, move each case study into its own subdirectory with index.md, and verify the list page renders. Check hugo server output for warnings about duplicate URLs or missing templates. If it works, roll the pattern out to other sections incrementally.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.