The complete guide to Hugo file structure and code organization


Depending on how you bootstrap your project, you may see any of the following folders and files inside your site directory. Understanding how the default structure is used and how the various file types relate can be confusing at first, but understanding it will bring you well along the path to becoming a hugo power user. This post breaks down how each directory is used, beginning with the bare minimum setup. I also try to point out relationships between sections, because that’s how I learn best.

# hugo full directory structure (more extensive than you will get by running hugo new site...)

First, the core essentials

content/ #markdown gets turned into pages, posts, etc
layouts/ #html templates provide structure
static/ #images, css, js files available at site root
config.toml #hugo configuration settings

Perfect for a quick landing page or starter blog

This is why I love Hugo. Using only a few basic files, you can layout a simple landing page or begin to work on a blog without wasting time on setup or configuration. Forget the WordPress “5 minute install”. Starting with these core pieces is only one step removed from writing everything in a single index.html file. At the same time, however, this minimal structure still allows you to keep content separate from layout.

Added bonus - when you decide to add a second page or find yourself tempted to cut and paste a particular html layout, it is really easy to begin creating components!

Just type up your content

Every file you put in the content directory becomes a page. Imagine you want to start a travel blog. All you have to do is create a travel folder and add a new markdown file for each post. Hugo compiles each post using a single layout and automatically creates an index for the travel folder using a list layout. If you want to customize the travel index page by adding a blurb about, say, why you became a travel blogger, you would use a file named in the travel directory.

  travel/ # list of travel posts generated at # full post generated # this is the default landing page for
  # I don't know why you would do this but... # this generates a page at

Content files are written in markdown. I recommend you just grab a cheat sheet and start using markdown, but for reference you could try Markdown Guide. Wes Bos also has a markdown video tutorial which I haven’t tried but do recommend. If you want to write inline html in your markdown, you can do that too.

Then combine content with a layout

Just a moment ago I used the words single and list - looking at templates, this is a key distinction to keep in mind.1 When you compile your site, Hugo expects default templates for both types of pages, and when creating custom templates both possibilities should be considered. Let’s look at the default building blocks.

    baseof.html # every page uses this to add the head, navigation, footer, etc.

baseof.html typically builds the basic layout by adding partial components like the head, navigation and footer. It also defines a block that will be filled by the appropriate template file, either single.html or list.html.

<!-- doctype.html -->
<!doctype html>
<html lang="en-US">
  {{ partial "head" . }}
  <body class="body">
    {{ partial "nav" . }}
    note the keywords block and define go together
    {{ block "main" . }}{{ end }} 
    {{ partial "footer" . }} 

<!-- single.html -->
{{ define "main" }}
  your html layout goes here
{{ end }}

In the case of a list page, list.html would replace single.html. At its most basic, this file uses a loop to call li.html for each item in the list. It can also set up list order or pagination using Hugo’s Paginator.

<!-- list.html -->
{{ define "main" }}
    {{ range (.Paginator 6).Pages.ByPublishDate.Reverse }}
      list item layout goes here, or it can be put in a separate file and rendered
      {{ .Render "li"}}
    {{ end }}
  {{ template "_internal/pagination.html" . }}
{{ end }}

Using a custom layout

Hugo provides options to use a custom layout for all pages of a particular type (for example, using a custom template for all blog posts in the travel category) or to specify a layout in the front matter for a particular post (imagine I want my post about spain to use a ‘video’ template - yes, this is equivalent to WP custom post types). The template lookup order can be complicated but the official documentation gives lots of examples.

Next, the rest of the basics

archetypes #used by hugo for command line generation of content files
data #files holding global data like author bios
themes #yes, hugo enables the use of prebuilt themes

If you create a new site from the command line, something like hugo new site, these are the folders you will see created.

Archetypes: if you don’t use a CMS

If you are using some sort of CMS, you don’t need these. But if you write content in an editor, archetypes can help. They define any fields (and field default values) a content file should have. This means meta information like publish date, author, tags, etc. as well as anything else you would typically put in the front matter. Default values can be set up in an archetype and automatically added when a new page is created from the command line.

Hugo documentation has some great examples you can copy if you decide to use archetypes.

Data: structured content

Data and content are very similar. Together they replace 90% of everything that gets put in the database when working with WordPress or another traditional CMS. Data can best be understood by thinking about how it differs from content.

content data
each file creates a page data files don’t create pages
everything in the file shows on the page often used partially
unstructured content Ok content must be structured
written in markdown (and html) written in json / toml / yaml

Just by looking at this table you probably have a feel for when you would use data instead of content. You might use data for snippets of information that get included in multiple places, for example. I recently found data files useful when building a list of senior leadership included photos, names in multiple languages, and optional short biographies. Something similar could be done on a blog for pulling author data into the end of each post.

Themes: using other’s code but overriding as necessary

If you’ve read this far, you probably aren’t interested in using someone else’s theme. You might nonetheless find themes useful if you want to use separate github repos for your site theme and content. I haven’t done this, but I suspect a submodule would have this effect.

The key thing to understand about themes is that they are organized just like your site folder! They have their own layouts, static, etc. But if you put a particular layout file in your site, it will override the theme. This makes sense when you think about it.

Sorry if I make too many comparisons to the WP ecosystem, but this is essentially like using a child theme. If you didn’t make the theme, you don’t make modifications to the theme because you will loose all your modifications every time you update. In the same way, a custom layout specific to your blog won’t be lost if you decide to update or change themes.

Finally, completely optional but very useful stuff

assets/ #source directory for pre-compilation resources
config/ #an alternative to config.toml if you have lots of site configuration
i18n/ #yaml files for translation of strings used in templates
resources/ #destination directory for compiled resources

Assets turn into resources

This directory is optional, but strongly recommended. Chances are you started looking at Hugo because you want something fast. Static sites are fast out of the box, but Hugo supports so much more that can be done to crank up the speed - things like image optimization, css minification, svg sprites… These techniques are complicated enough that I’m not going to begin explaining them here.

Suffice it to say, if you have source files (scss, js, high resoluation images, etc) that are transformed before they get used, they live in this directory. The cropped/compiled/linted/minified/your-transformation-of-choice versions get automatically put into the resources/ directory. The only thing to know about that directory is that occasionally it needs to be purged because it doesn’t have automatic garbage collection.

i18n: do you speak
English English

This folder holds files named languagecode.yml which are needed to build multi language sites. These files provide translations for strings used in template files. To support translations, hardcoded text like “copyright 2019 Jonathan Droege” should be replaced with a string Id like {{ i18n copyrightStringId }}.

Config: for when you have a big site

If you need enough configuration settings that a single file becomes cumbersome, you can replace it with multiple files in a config/ directory.

  1. In general the Hugo documentation is pretty good, but I actually found this confusing for a long time because section pages is used interchangebly with list pages. Technically, sections (categories) generate a list page by default, as do other taxonomy like tags, author, etc. [return]