Single and List Layouts
Authors
Custom Single and List Layouts
Hugo uses different layout files to control how different types of pages appear. Single pages like About or Contact use single.html, while list pages like blog archives use list.html. Template inheritance with baseof.html allows sharing common HTML structure across all pages. Hugo also supports page-specific layouts for customizing individual sections.
Here we will see:
- how to create single and list layouts
- how to use template inheritance with base templates
- how to create page-specific layouts
Section 1: Single and List Layouts
Background
Hugo distinguishes between two main types of pages: single pages and list pages. Single pages display individual content like an About page or blog post. List pages display collections of content like a blog archive or project gallery. The single.html layout controls how individual pages appear, while list.html controls how collections of pages appear. Hugo automatically chooses the appropriate layout based on the page type. Single pages use the markdown content directly, while list pages can loop through child pages to create navigation or archives.
Exercises
This section covers creating layouts for single pages and list pages.
| Concept | Description |
|---|---|
layouts/_default/single.html |
Layout for individual content pages |
layouts/_default/list.html |
Layout for collection pages |
{{ .Content }} |
Page content from markdown |
{{ .Title }} |
Page title from front matter |
{{ .Description }} |
Page description from front matter |
{{ .Date }} |
Page date from front matter |
{{ .RelPermalink }} |
Relative URL to page |
{{ range .Pages }}...{{ end }} |
Loop through child pages |
<a href=""></a> |
Link tag |
<ul></ul> |
Unordered list tag |
<li></li> |
List item tag |
<div></div> |
Division tag for grouping |
<h2></h2> |
Level 2 heading tag |
<p></p> |
Paragraph tag |
Single pages are the most common page type in Hugo. They display individual content from markdown files. The single.html layout wraps the markdown content with HTML structure. Creating a basic single.html layout allows Hugo to display any content page on the site.
Example: Create a basic single.html layout and an about page
Create content/about.md:
+++
title = "About Me"
+++
I am a web developer specializing in Hugo static sites.Create layouts/_default/single.html:
<body>
{{ .Content }}
</body>Exercise: Change the content in about.md and verify single.html applies to the changes.
Update content/about.md:
Solution
+++
title = "About Me"
+++
I am a web developer specializing in Hugo static sites. I have been building websites for 5 years.Exercise: Create contact.md with content and verify single.html applies to it.
Create content/contact.md:
Solution
+++
title = "Contact"
+++
Email: hello@example.comList pages display collections of content. Hugo automatically treats directories as list pages when they contain an _index.md file or child pages. The list.html layout can loop through all child pages using the range function. This allows creating navigation menus, blog archives, or project galleries automatically.
Example: Create a list.html layout that displays page titles
Create layouts/_default/list.html:
<body>
{{ range .Pages }}
{{ .Title }}
{{ end }}
</body>Exercise: Print the link to each page instead of the title.
Solution
<body>
{{ range .Pages }}
{{ .RelPermalink }}
{{ end }}
</body>Exercise: Make the titles clickable links.
Solution
<body>
{{ range .Pages }}
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
{{ end }}
</body>HTML structure improves the readability and organization of list pages. Using semantic HTML tags like lists and divs helps both users and search engines understand the content structure. Adding additional page information like descriptions and dates makes the list more informative.
Example: Display page titles as an unordered list
<body>
<ul>
{{ range .Pages }}
<li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li>
{{ end }}
</ul>
</body>Exercise: Add the page description below each title.
Solution
<body>
<ul>
{{ range .Pages }}
<li>
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
<p>{{ .Description }}</p>
</li>
{{ end }}
</ul>
</body>Exercise: Wrap each page in a div and add the publication date.
Solution
<body>
{{ range .Pages }}
<div>
<h2><a href="{{ .RelPermalink }}">{{ .Title }}</a></h2>
<p>{{ .Description }}</p>
<p>{{ .Date.Format "January 2, 2006" }}</p>
</div>
{{ end }}
</body>Section 2: Template Inheritance
Background
Template inheritance in Hugo allows sharing common HTML structure across multiple layouts. The baseof.html file serves as a base template that defines the overall page structure. Other layouts like single.html and list.html extend the base template by defining specific blocks. This approach eliminates code duplication because common elements like headers and footers only need to be written once. Blocks in the base template can have default content that appears when child templates do not override them. Child templates can choose which blocks to override while inheriting the rest of the structure.
Exercises
This section covers creating base templates and using template inheritance.
| Concept | Description |
|---|---|
layouts/_default/baseof.html |
Base template for all pages |
{{ block "name" . }}...{{ end }} |
Define a block with default content |
{{ define "name" }}...{{ end }} |
Override a block in child template |
<html></html> |
HTML document root tag |
<head></head> |
HTML head section |
<body></body> |
HTML body section |
<header></header> |
Header section tag |
<footer></footer> |
Footer section tag |
<article></article> |
Article content tag |
The base template defines the overall HTML structure that all pages share. Blocks mark areas where child templates can insert custom content. When a block has content inside it, that content serves as the default if no child template overrides it. Child templates must define blocks using the same names to override them.
Example: Create a base template with a main block and update single.html and list.html to use it
Create layouts/_default/baseof.html:
<html>
<head>
<title>{{ .Site.Title }}</title>
</head>
<body>
{{ block "main" . }}
{{ .Content }}
{{ end }}
</body>
</html>Update layouts/_default/single.html:
{{ define "main" }}
{{ end }}Update layouts/_default/list.html:
{{ define "main" }}
{{ end }}Exercise: Change the default content in baseof.html to show title before content.
Update layouts/_default/baseof.html:
Solution
<html>
<head>
<title>{{ .Site.Title }}</title>
</head>
<body>
{{ block "main" . }}
<h1>{{ .Title }}</h1>
{{ .Content }}
{{ end }}
</body>
</html>Exercise: Delete single.html and list.html, observe what happens, then add them back.
Delete layouts/_default/single.html and layouts/_default/list.html and run Hugo. Pages will fail to render properly. Recreate both files with the empty define blocks shown in the previous example.
Child templates can override blocks in the base template by defining their own content for those blocks. This allows customizing specific parts of the page while keeping the shared structure. Overriding a block completely replaces the default content from the base template.
Example: Override the main block in single.html turning title to level 3 heading
Update layouts/_default/single.html:
{{ define "main" }}
<h2>{{ .Title }}</h2>
{{ .Content }}
{{ end }}Exercise: Override the main block in list.html to show a page list.
Update layouts/_default/list.html:
Solution
{{ define "main" }}
<h1>{{ .Title }}</h1>
<ul>
{{ range .Pages }}
<li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li>
{{ end }}
</ul>
{{ end }}Exercise: Add a paragraph with the date above the content in single.html.
Update layouts/_default/single.html:
Solution
{{ define "main" }}
<h1>{{ .Title }}</h1>
<p>Published: {{ .Date.Format "January 2, 2006" }}</p>
{{ .Content }}
{{ end }}Base templates can contain multiple blocks to allow child templates to customize different sections independently. Common blocks include header, main, sidebar, and footer. Each block can have its own default content. Child templates can choose to override some blocks while leaving others as default.
Example: Add a header block to baseof.html
Update layouts/_default/baseof.html:
<html>
<head>
<title>{{ .Site.Title }}</title>
</head>
<body>
{{ block "header" . }}
<header>
<h1>{{ .Site.Title }}</h1>
</header>
{{ end }}
{{ block "main" . }}
{{ .Content }}
{{ end }}
</body>
</html>Exercise: Override the header block from single.html and list.html.
Update layouts/_default/single.html to add header override:
Solution
{{ define "header" }}
<header>
<h1>{{ .Site.Title }}</h1>
<p>Article</p>
</header>
{{ end }}
{{ define "main" }}
<h1>{{ .Title }}</h1>
{{ .Content }}
{{ end }}Update layouts/_default/list.html to add header override:
Solution
{{ define "header" }}
<header>
<h1>{{ .Site.Title }}</h1>
<p>Archive</p>
</header>
{{ end }}
{{ define "main" }}
<h1>{{ .Title }}</h1>
<ul>
{{ range .Pages }}
<li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li>
{{ end }}
</ul>
{{ end }}Exercise: Add a footer block in baseof.html and override it from single.html and list.html.
Update layouts/_default/baseof.html to add footer block:
Solution
<html>
<head>
<title>{{ .Site.Title }}</title>
</head>
<body>
{{ block "header" . }}
<header>
<h1>{{ .Site.Title }}</h1>
</header>
{{ end }}
{{ block "main" . }}
{{ .Content }}
{{ end }}
{{ block "footer" . }}
<footer>
<p>© 2025</p>
</footer>
{{ end }}
</body>
</html>Update layouts/_default/single.html to add footer override:
Solution
{{ define "header" }}
<header>
<h1>{{ .Site.Title }}</h1>
<p>Article</p>
</header>
{{ end }}
{{ define "main" }}
<h1>{{ .Title }}</h1>
{{ .Content }}
{{ end }}
{{ define "footer" }}
<footer>
<p>© 2025 | Single Page</p>
</footer>
{{ end }}Update layouts/_default/list.html to add footer override:
Solution
{{ define "header" }}
<header>
<h1>{{ .Site.Title }}</h1>
<p>Archive</p>
</header>
{{ end }}
{{ define "main" }}
<h1>{{ .Title }}</h1>
<ul>
{{ range .Pages }}
<li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li>
{{ end }}
</ul>
{{ end }}
{{ define "footer" }}
<footer>
<p>© 2025 | List Page</p>
</footer>
{{ end }}Section 3: Page Specific Layouts
Background
Hugo allows creating custom layouts for specific pages or sections. Layouts in section-specific directories take precedence over default layouts. For example, layouts/about/single.html will be used for pages in the about section instead of layouts/_default/single.html. This hierarchy allows customizing individual sections without affecting the rest of the site. The type parameter in front matter can also specify which layout directory to use. Hugo searches for layouts in a specific order, checking section directories before falling back to defaults. This flexible system supports sites with varied page designs while maintaining organized code.
Exercises
This section covers creating section-specific and type-specific layouts.
| Concept | Description |
|---|---|
layouts/section/single.html |
Layout for specific section |
layouts/section/list.html |
List layout for specific section |
type = "value" |
Front matter field to specify layout type |
{{ .Summary }} |
Page summary or excerpt |
content/section/_index.md |
Index file for list pages |
| Layout lookup order | Section layouts override default layouts |
Section-specific layouts override default layouts for pages in that section. Creating a directory matching the section name in layouts allows customizing just those pages. Hugo automatically uses the section-specific layout when it exists, falling back to the default if it does not.
Example: Create a custom layout for the about section
Create layouts/about/single.html:
{{ define "main" }}
<h1>About Section</h1>
<h2>{{ .Title }}</h2>
{{ .Content }}
{{ end }}Exercise: Create a custom layout for contact that differs from about.
Create layouts/contact/single.html:
Solution
{{ define "main" }}
<h1>Contact Page</h1>
<h2>{{ .Title }}</h2>
{{ .Content }}
{{ end }}Exercise: Remove the contact layout and verify default single.html applies.
Delete layouts/contact/single.html and verify that content/contact.md now uses layouts/_default/single.html.
List pages can also have section-specific layouts. Creating a list.html file in a section directory customizes how that section displays its child pages. This allows different sections to have different list styles, such as a blog with summaries versus a portfolio with thumbnails.
Example: Create a custom list layout for the blog section
Create content/blog/_index.md:
+++
title = "Blog"
+++Create layouts/blog/list.html:
{{ define "main" }}
<h1>Blog Posts</h1>
{{ range .Pages }}
<div>
<h2><a href="{{ .RelPermalink }}">{{ .Title }}</a></h2>
<p>{{ .Summary }}</p>
</div>
{{ end }}
{{ end }}Exercise: Create a custom list layout for projects that differs from blog.
Create content/projects/_index.md:
Solution
+++
title = "Projects"
+++Create layouts/projects/list.html:
Solution
{{ define "main" }}
<h1>My Projects</h1>
<ul>
{{ range .Pages }}
<li>
<a href="{{ .RelPermalink }}">{{ .Title }}</a> - {{ .Description }}
</li>
{{ end }}
</ul>
{{ end }}Exercise: Remove the blog list layout and verify default list.html applies.
Delete layouts/blog/list.html and verify that content/blog/ now uses layouts/_default/list.html.