How I Organize My Hugo Site
The best Hugo project structure is not the cleverest one. It is the one I can open after six months and understand without searching through three abstraction layers.
I stay close to Hugo’s standard directory structure and introduce a new convention only when the site has a repeated problem.
Let content resemble the public site
content/
├── blogs/
├── projects/
└── about/
When an article owns images or downloads, I use a page bundle:
content/blogs/my-article/
├── index.md
├── hero.jpg
└── diagram.svg
Hugo’s page bundle documentation explains the resource model. Keeping an article and its resources together is easier than matching it to files in one enormous global image directory.
Separate copied files from processed assets
I put files that should be copied as-is in static: icons, verification files, and standalone downloads are common examples.
Files Hugo should process belong in assets:
assets/
├── css/
│ └── main.css
└── js/
└── main.js
One main CSS entry point can import smaller files without making the template responsible for loading a dozen unrelated stylesheets.
Keep templates named after their job
layouts/
├── _default/
├── blogs/
└── partials/
├── header.html
├── footer.html
└── article-card.html
I prefer partials that represent understandable pieces of the page. Splitting every two lines into a partial creates navigation overhead; leaving the whole site in one template creates a different kind of overhead.
Template logic should also remain obvious. If a direct range expresses the page list, I do not hide it behind several layers solely to make the project look architectural.
Keep configuration and URLs deliberate
Configuration grows quickly when every small value moves out of the template. I keep genuine site-wide settings there and leave local presentation close to the code that uses it.
Content paths, slugs, and aliases affect public URLs, so reorganising files deserves care. A tidier repository is not worth breaking old links.
I also use Git history instead of keeping header-old.html, header-final.html, and the inevitable header-final-2.html. Deleted code remains recoverable without living beside the current version.
My test is whether the directory tree can answer three questions quickly: where is the content, where is its resource, and which template renders it? A structure that does that will usually age better than one built around every feature Hugo happens to support.
Enjoyed reading this?
If you found this article helpful, consider supporting my writing. Every little bit helps keep this blog going.
Related articles
- When to Use Hugo Shortcodes or Render Hooks
Use render hooks to change normal Markdown output and shortcodes for custom content with meaning Markdown cannot express.
- Customizing Hugo’s RSS Feed
Adjust Hugo RSS titles, descriptions, content, and section filtering while keeping the generated feed valid and easy to discover.
- Organizing Hugo Content With Tags and Categories
Organize Hugo content with a small, deliberate set of tags, categories, or custom taxonomies that lead to useful archive pages.