When I first started working with Hugo I had some difficulty grasping the relationships between certain directories in my project. It can feel like there is a lot of abstraction going on in the background that you're not aware of. Things like naming conventions and lookup orders that you wouldn't know about without diving headfirst into the official documentation.
It took some time, but I can definitely say that the architectural decisions made by the Hugo team feel so intuitive. The default directory structure is light, containing only a few folders at the project's root along with a config file holding some meta information. When you start a new project it will look like this:
archetypes/ content/ data/ layouts/ static/ themes/ config.toml
This post goes over what I consider to be the core of the Hugo directory:
I'll explain each one in order of "most useful", which is completely subjective, but understanding a certain directory's purpose will help you understand the purpose of the next.
Hugo allows for theme creation and they have a number of community-built themes that you can choose from if you don't feel like doing the leg work. This is apparent when going through their Quick Start guide.
Working with existing themes or building your own adds an additional layer to the directory structure of your project. It is not something that I will cover in this post. If you're looking to build a theme or alter an existing one then this post may not be for you. That being said, if you understand how the above directories work you'll have no issues working with themes in Hugo.
content/ directory holds all the content of your site.
Who would've thought.
Seriously though, I'm writing this post in the
content/ directory right now.
It is made up of the sections and static pages that structure a website.
There's a distinct difference between the terms sections and static pages and it's pretty easy to grasp:
A section is a portion of the site that holds subcontent, such as a blog section and a work section. The blog section contains individual blog posts, and the work section is a portfolio of individual projects.
A static page is simply a page that renders content and nothing else. It doesn't need to index a list of subcontent for the user to peruse. The about page is a good example. If you view it in the browser, you can't go down any further. It's the about page and that's it.
So, if you were tasked to create a simple website containing this structure:
content/ directory would potentially look like this:
content/ about.md blog/ contact.md work/ \_index.md
The about and contact pages are static and are therefore defined as
at the root of
content/. Blog and work are sections of the site containing
subcontent, so they are structured as directories.
_index.md file plays an important role here. Hugo requires that an
_index.md file be explicity defined at the root of
content/, and it uses
this file to render the homepage of your site.
All markdown files that exist inside
content/, whether at the root or in
subdirectories, have a
type associated to them. Hugo infers a markdown file's
type in one of two ways:
- The name of the subdirectory that the file resides in
typevariable defined in that file's front matter (optional)
The former is pretty simple to understand. Hugo will initially deduce a markdown
file's type by simply looking at the name of the subdirectory it is in. The file
a-blog-post.md located at
content/blog/a-blog-post.md will have the type
blog. If it were located at
content/posts/a-blog-post.md it would have the
You can override this behavior by defining the
type variable inside of the
file's front matter. We
won't be explaining front matter in this post, but it's basically metadata
defined at the top of a file such as its
There are a lot of cool things you can do with front matter and I'll list a few
later on, but just know that you can explicitly define a file's type here and it
will override the default type inferred from the subdirectory it is in.
So if you're wondering: Yes. You can have a file at
content/blog/a-blog-post.md with a type explicitly set to
Another concept to understand about the markdown files that exist in the
content/ directory is that they can represent list pages or single
pages. To illustrate this, let's continue to use the example given above.
You now know that the blog section must be defined as a subdirectory of
content/. It will contain individual blog posts written as
content/ blog/ a-blog-post.md another-blog-post.md \_index.md
Notice that there are two posts and an
_index.md file inside of
posts represent single pages in the blog section. The purpose of
is to index these posts and is therefore considered a list page.
This concept holds true for all subdirectories of
content/. Each one must
_index.md file which gains access to all single pages in that
section. This access grants the
_index.md file the ability to list out all the
If you navigate to
_index.md file will be used as
the content for that specific route. It will render a list of blog posts that
exist in the blog section. Pretty neat, huh?
layouts/ directory is nothing more than a directory of templates. Each
provides a consistent layout when rendering the markdown files that exist in
content/. It is, in my opinion, the most crucial directory in a Hugo project.
I feel this way because although Hugo has a set of rules as to how templates
should work, the developer can implement them in a number of ways.
Are you starting to see a trend? Lists and singles are one of the foundations of
Hugo and they are at the heart of the
layouts/ directory. List templates and
single templates do exactly as they sound: render list pages and single pages.
To understand the
layouts/ directory you must understand how Hugo resolves
which template to use to render content. It does this by using a lookup
order, or a list of filepaths to inspect, hoping to find an appropriate
In theory, your project could run on only two templates: a single and a list
template. This is sufficient enough to handle all the content on your site. Your
layouts/ directory would then look like this:
layouts/ \_default/ single.html list.html
Hugo looks in the
_default/ directory if it cannot find any other template to
use to render a specific piece of content. Think of
_default/ as Hugo's "last
resort". A quick tip: when creating a Hugo project for the first time your
layouts/ directory will be empty. This means that you must create the
default/ subdirectory yourself.
If you decided to go this route it would mean that all list pages would render
with the same template. This holds true for single pages as well. Why? Well,
you've only given one template for each page type. Hugo will just resolve to
using that template when it finds itself at the
/default directory during the
This may not seem like an issue until you want to structure your
content/blog/_index.md list page differently from your
content/work/_index.md list page. How would you handle that?
Since Hugo gives you a number of ways to create "different" pieces of content,
it must also give you a way to uniquely template them. Take a step back and
think about how the
content/ directory was structured:
content/ about.md blog/ contact.md work/ \_index.md
Remember content types? This is where they start to come in handy. If you want
to use unique templates for specific content types, you can create
layouts/ for each type. For example, if you wanted the
blog list page and all the blog single pages to use a separate set of templates
from the rest of your site, you could define those templates in a
layouts/ \_default/ single.html list.html blog/ single.html list.html
During the lookup order Hugo will resolve to use
template for the blog list page and
layouts/blog/single.html for all of the
blog posts. Everything else will just inherit the templates inside
Let's assume that you want to extend this template specificity for every content
type on your site. Your
layouts/ directory would continue to "mirror" your
content/ directory by accepting subdirectories named after each content type.
layouts/ \_default single.html list.html blog/ single.html list.html work/single.html list.html
Looking good, but what about the about page and the contact page? They would
currently be rendered using the
_default templates. That is sufficient enough,
but I'm going to push a personal strategy on you:
I think its best to explicitly define templates for each content type you have on your site.
So let's do that. But first, a question. What content type does the about page and the contact page actually have? Remember how we said that content types are determined in one of two ways:
content/subdirectory it is in
typevariable defined in the file's front matter
Well, the about page and contact page aren't in a subdirectory of
number one doesn't help us. But, we can appease number two by defining a content
type in each page's front matter.
As I said before, this post doesn't cover front matter in depth, but this
occasion requires a slight detour. For content pages that live at the root of
content/, I like to explicitly define the
type variable inside their front
matter with a value of
// content/about.md & content/contact.md---## type: "static"
I got this idea while reading one of Sara Soueidan's blog posts about migrating her site to Hugo. It was a massive help for me at the beginning.
Once we define the
type variable inside a file's front matter we can create a
layouts/ in order to template those files directly:
layouts/ \_default single.html list.html blog/ single.html list.html work/single.html list.html static/ single.html list.html
This paradigm is really powerful and makes working with Hugo an absolute breeze.
There's another page inside of
content/ that we haven't discussed: The
homepage. Just like the homepage gets its own markdown file inside of
content/, it too can have its own template file:
layouts/ \_default single.html list.html blog/ single.html list.html work/single.html list.html static/ single.html list.html index.html
Hugo doesn't require this, but I like to err on the side of explicit where
possible. If you forego a
layouts/index.html file, Hugo will then look to
layouts/_default/list.html to render the homepage. At first I didn't
understand why. I figured the homepage was a single page and would be rendered
with a single template. But it's not.
It's actually a list page. In the
content/ directory the homepage is
_index.md, sharing the same name with all other list pages in
subdirectories. It lists that which exists at the root of
facto your entire website.
Here's an interesting tip that took a while for me to discover in the Hugo docs:
You can have a base template which all other templates inherit from. This
template is the Queen Bee, and she is named
There can actually be a number of these
baseof.html files in your
Hugo's lookup order for base templates
can get a bit confusing. That's why I choose to have only one located at
This file is the master template that contains a lot of the standard html I wouldn't want to include in every template. Things like the document head, stylesheets and js files. I declare them once and all other templates inherit from it.
I'll be writing a post specifically on the relationships between all of these templates soon so be on the lookout for it!
One of Hugo's main draws is that it gives the developer the ability to create
new types of content quickly. Archetypes make this possible. The
directory can contain markdown files named after content types on your site.
Examples of this would be:
work.md, and of course,
Inside these "archetypes" are default parameters defined via front matter that
every new piece of content associated with this archetype will inherit. When you
first start a new Hugo project the
archetypes/ directory will only contain a
default.md file. All new content files generated via the command line will
inherit from this archetype unless an archetype exists specifically for that
You can generate new content files using the
hugo new command:
hugo new <content_type>/<file_name>
Hugo will traverse the
archetypes/ directory to see if any archetypes match
the value of
<content_type>. If none are found it will use
generate the file.
If you wanted to have all of your blog posts contain specific front matter
separate from other content types, and assuming your blog posts live at
content/blog/, you would define a
blog.md archetype with the desired front
matter defined. Then create a new blog post from the command line:
hugo new blog/a-new-post.md
A file at
content/blog/a-new-post.md will be created that contains all
pre-defined front matter.
Maybe your blog posts have a
description parameter and a
that other content types don't have. Maybe you don't want to manually create
these files by hand every single time. It comes in handy and makes your life as
a developer that much easier. Automation for the win!
static/ directory is pretty straightforward. All assets are placed here
and can then be used throughout the project.
static/ css/ styles.css js/ index.js
Using the structure above, you can reference both files using a filepath
relative to the
static/ directory such as
One of the things Hugo lacks is an asset pipeline. It doesn't have any opinions on how you should handle your css, js, images, and fonts. All that matters is that the final assets are in this folder when you want them to be usable on the site. How they get there is up to you.
I opted for a simple Gulp workflow, writing all of my css and js in a
directory, watching for changes, and compiling->outputting to
static/. I'll be
writing a post on specifics soon as well.
I hope this post has proved useful to you on your quest to master Hugo. It is still a very new tool for me, but I've been digging my initial experience. The directory itself is minimal and that's one of the things I enjoy about it. The less mental overhead the better, right?
You can do some pretty awesome things once you understand how these core directories are working with each other. There are projects that exist such as Victor Hugo that take Hugo's architecture to the next level. This is a project you'll definitely want to check out if you are interested in an advanced workflow.
I'll continue to post regularly about some tips and tricks I learn along the way. If you have any questions or want to correct a mistake in this post, or even if you just want to talk shop, feel free to reach out to me via Twitter!