Layouts in Hugo

Learn how layouts help you set up and reuse the main structure of your Hugo site.

What you'll learn here

  • Hugo’s layout naming and structure conventions

  • What “lookup order” means

  • How to create and use some basic layouts

  • How to improve the look of your pages

# Starting repo
git clone

# Starting branch:
git checkout layouts-intro-start

# Finished branch:
git checkout layouts-intro-finish

Subsequent lessons will focus on creating a very basic but usable site with Hugo concepts and Go templating. HTML, CSS, and JavaScript are primarily pre-made and not a focus of the lessons.

What's a layout?

A layout is any sort of page used as a “frame” to display your content. Think of it as the parts of the website that remain consistant across all of the pages.

Hugo has strict conventions for layout pages. Let’s take a look at some of the most used conventions.

Layout conventions

The names of HTML files, and the directories where they live, must follow Hugo naming conventions. This might all seem like a lot of effort, but it’s good practice and allows you to get very specific and granular with your pages.

Recognized layout names

Here are some of the most common layout names that Hugo recognizes:

  • baseof.html - the base template for a site or section of a site.
  • list.html - the template used to list content in a section of your site (e.g., /posts)
  • single.html - the template used to display a single page (e.g., posts/first-post)

Directory names

The first and most important folder in layouts is _default. This is where the final fallback layouts live for your content. Typical examples of pages here are _default/baseof.html and _default/single.html - the base template for all pages, and a base template for all single display pages.

The name of the folder in which your other layouts live is also important for Hugo. The name you choose will be the type(a topic for later) that Hugo assigns it, and it should typically match the names for your content.



Nesting layouts - block and define

Having a layout is great and all, but if we have to redefine the entire layout every time we want to change one section, it’s going to be a nightmare to maintain. Hugo has a simple way of handling layout inheritance to avoid this issue. First, We can define a block in the parent layout:

  {{ block "main" . }}
  {{ end }} 

Next, and on the child layout, we can define what the main block should be:

{{ define "main" }}
  <h1>This will be inserted into the block</h1>
{{ end }} 

Important notes:

The “dot” is necessary when creating a block (not when using it). This refers to the current context. We’ll explain this in detail later.

You can have multiple blocks on a page, but you can’t nest a block inside another block.

Lookup order - what does that mean?

One additional feature in Hugo templates is “lookup order”. The Hugo docs explain this well in detail, but it’s quite a lot to consume, so let’s boil it down:

Simplified example:

Let’s say you’ve created content such as content/blog/ (a single page) and content/blog(a directory of posts). When you build/serve your site, in order to display your content, Hugo will look through your templates, in a defined order:

Displaying a single page (/blog/my-first-post)

  • Is there a specific “single-page” layout (e.g., single.html) in layouts/blog?
  • If not, is there a specific “single-page” layout in the _default folder?

Displaying a directory page (/blog):

  • Is there a “list template” (e.g., list.html) with logic to list the contents of a directory in layouts/blog?
  • If not, is there a specific “list template” layout in the _default folder?

In either case, if a suitable layout isn’t found, your content probably won’t be displayed.

Practical work: creating layouts

This might be quite a bit to digest, so let’s actually just do something practical. If you haven’t checked out the project code at the top of the page, now’s the time.

Notice that the project is still mainly empty, so let’s start putting it together.

Default and single-page layouts

  • Add a folder called _default to the layouts folder.
  • Add /layouts/_default/baseof.html and move the contents of /layouts/index.html into this file. Now, inside /layouts/_default/baseof.html, where it says <!-- Add block here -->, add the following:
{{ block "main" . }}{{ end }}
  • Now let’s change the contents of /layouts/index.html this:
{{ define "main" }}
  <div class="hero">
    <h1 class="hero__header dark-orange">CawCannon</h1>
  <h2>Reasons why Hugo is awesome</h2>
    <li>It's the fastest static site generator.</li>
    <li>It does everything you need in one package.</li>
    <li>It'll save you time and build costs.</li>
    <li>It helps you create lean, well-structured sites.</li>
{{ end }}

Now our /layouts/index.html is much smaller, and is using the frame (block) of our /layouts/_default/baseof.html file. Anything between define and end will now be injected into our baseof.html block. This helps us reduce repetition by “nesting” content.

  • For future usage, let’s also set up a simple single-page layout for our content, which will also use our baseof.html template. By default, Hugo will look for single.html pages when creating single-page content, so this will be an important backup for later pages. Add /layouts/_default/single.html with this content:
{{ define "main" }}
  {{ .Content }}
{{ end }}

Note the .Content variable. This refers to any content below front matter from inside content files (discussed more when we look at content and blogging).

  • Use the Hugo CLI with hugo -D server and inspect the site at localhost:1313. Feel free to make any modifications and experiment!

Improving our visuals

The site is looking a little bit like a 90s static site. Let’s start our journey into modern static sites with some improved styles.

You might notice that there’s an assets folder with a single scss/main.scss file in it. In this tutorial, we’re using some basic SCSS, because it is a superset of CSS which offers a lot of extra utility. However, it does require something to process it back into normal CSS, because browsers don’t understand SCSS files. Fortunately, Hugo offers this ability by default if you’ve got the extended version (recommended - run hugo version and visit Hugo install if you want it). We do this by putting our SCSS file into our build pipeline with Hugo Pipes.

Add this to the <head> section of _default/baseof.html:


{{ $style := resources.Get "sass/main.scss" | resources.ToCSS | resources.Minify }}
<link rel="stylesheet" href="{{ $style.Permalink }}">

What exactly does this do? With this basic pipe, Hugo will take the SCSS file from assets/sass/main.scss, process it into normal CSS (resources.ToCSS), and make its file size as small as possible (resources.Minify). The resulting data will then be available as an ordinary CSS file, which you can link to in the head of your HTML as normal (with href="{{ $style.Permalink }}").

Reload the page and see the results. You might also notice a resources folder appear.

Concepts: assets, resources, static, pipes

At this point it might be helpful to describe some concepts in a Hugo project a bit more:

  • The assets folder is where you put any assets you want Hugo to process (SCSS and images are common examples).
  • The resources folder is simply created by Hugo to cache processed assets. Don’t worry about this folder (in fact, if you have.gitignore file, you might want to add resources as an entry).
  • The static folder is where you put any assets that you don’t want to process (e.g., JS, CSS, images you’ve already optimized or don’t need to touch).
  • Hugo Pipes are how Hugo does extra things during a site build. We used a simple one for processing SCSS here, but there’s a lot more you can do, such as image and JavaScript optimization.

What's next?

This is quite a lot of content to take in, but hopefully you now understand some of the important concepts of layouts in Hugo.

Feel free to continue experimenting here before moving on, as understanding layouts, the file structure, and lookup order might take more than a few seconds to wrap your head around!

Next, we’ll deal with the unlocking Hugo’s built-in templating language for improving static sites.