Alright, let’s get our hands dirty with layouts/. This is where Hugo stops being a simple content organizer and transforms into a proper static site generator. Think of this directory as your workshop, filled with blueprints (templates) that dictate how every single page on your site will be built. Without these, Hugo would just have a pile of lumber and nails (your content) with no idea how to assemble a house.

The core concept here is Hugo’s lookup order. This isn’t just some academic detail; it’s the absolute master key to understanding how the engine works. When Hugo goes to render a piece of content, it goes on a very specific hunt through your layouts directory to find the right template. It’s looking for the most specific blueprint possible. The order is: 1) layouts/TYPE/single.html, 2) layouts/_default/single.html, and finally 3) themes/THEME/layouts/_default/single.html. This means you can override the theme’s templates for a specific section by creating a more specific file. It’s Hugo’s way of saying, “I’ll use your general blueprint, unless you gave me a better one for this exact job.”

The Big Three: Single, List, and Base Templates

You’ll live in three main template files. Get to know them intimately.

layouts/_default/single.html: This is your workhorse. It renders a single page of content—a blog post, a project page, you name it. This is where you have access to all the page’s data via the . context. You’ll pull in the title (.Title), the content itself (.Content), and any other front matter you defined.

<!-- layouts/_default/single.html -->
<article>
  <h1>{{ .Title }}</h1>
  <p class="published">Published: {{ .Date.Format "January 2, 2006" }}</p>
  <div class="content">
    {{ .Content }}
  </div>
  {{ with .Params.tags }}
  <ul class="tags">
    {{ range . }}
    <li><a href="{{ "/tags/" | relLangURL }}{{ . | urlize }}">{{ . }}</a></li>
    {{ end }}
  </ul>
  {{ end }}
</article>

layouts/_default/list.html: This template renders list pages. The most obvious example is your homepage, which is a list of your latest posts. But it also handles section listings (e.g., all posts in your “blog” section), taxonomy lists (all tags, all posts with a specific tag), and so on. Here, the context loops over a collection of pages (.Pages or .RegularPages).

<!-- layouts/_default/list.html -->
<h1>All {{ .Type | pluralize }}</h1>
{{ range .Pages }}
  <h2><a href="{{ .RelPermalink }}">{{ .Title }}</a></h2>
  <p>{{ .Summary }}</p>
{{ end }}

layouts/_default/baseof.html: This is the architectural foundation of your entire site. It defines the root HTML structure—the <html>, <head>, and <body> tags—and includes blocks that your other templates will fill. This is where you put your CSS and JS includes once, not in every single template. Using {{ block "main" . }} {{ end }} creates a placeholder that single.html and list.html will populate.

<!-- layouts/_default/baseof.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>{{ .Site.Title }}</title>
  <link rel="stylesheet" href="/css/style.css">
</head>
<body>
  {{ partial "header.html" . }}
  <main>
    {{ block "main" . }} {{ end }}
  </main>
  {{ partial "footer.html" . }}
</body>
</html>

Your single.html would then use this base template and fill the “main” block:

{{ define "main" }}
<article>
  ... all your single page content from the first example goes here ...
</article>
{{ end }}

Partials: Your Best Friend for Sanity

Never, and I mean never, put your site’s header or footer HTML directly into your baseof.html or single.html. That way lies madness and unmaintainable code. This is what the partials/ subdirectory is for. Partials are reusable chunks of template code. They are the single greatest weapon you have against repeating yourself.

<!-- layouts/partials/header.html -->
<header>
  <nav>
    <a href="{{ .Site.BaseURL }}">Home</a>
    {{ range .Site.Menus.main }}
    <a href="{{ .URL }}">{{ .Name }}</a>
    {{ end }}
  </nav>
</header>

You then include them with the partial function, as shown in the baseof.html example above. If you need to change your navigation, you do it in one file. Thank me later.

Section-Specific Overrides: The Real Power Move

Here’s where Hugo’s lookup order becomes pure magic. Let’s say you have a “projects” section. You want those pages to look different from your blog posts. You don’t need to write convoluted logic in your single.html; you just create a more specific template.

Create layouts/projects/single.html. Now, any page with type: projects in its front matter (or living in the content/projects/ directory) will use this template instead of the generic _default/single.html. You can make it look completely different—show a project gallery, a GitHub link, whatever—without touching your blog post template. It’s clean, logical, and incredibly powerful. This is the design choice that makes Hugo so flexible. Use it.