Base Templates, Partials, and Lookup Order

Base Templates, Partials, and Lookup Order

Authors
Sangeetha Nandakumar | Nicholas Del Grosso

Hugo uses templates to build website layouts. Templates can inherit from other templates, use reusable components, and follow specific rules for which template to use.

Here we will see:

  • how to create base templates
  • how to use partials for reusable components
  • how Hugo decides which template to use

Section 1: Base Template

Background

Base templates define the common structure for a website. Other templates inherit from the base template and can override specific sections. Base templates are created at layouts/_default/baseof.html. The homepage layout at layouts/index.html inherits from the base template.

Exercises

This section covers creating base templates and using blocks for overridable sections.

Concept Description
layouts/_default/baseof.html Base template file
layouts/index.html Homepage layout
{{ block "name" . }} Define block for override
{{ define "name" }} Override block content
.Site Access site variables
.Title Page title

Base templates allow multiple pages to share the same HTML structure. The base template is created at layouts/_default/baseof.html. The homepage at layouts/index.html inherits from it.

Example: Create base template with Welcome message

Create layouts/_default/baseof.html:

<html>
<head>
    <title>My Site</title>
</head>
<body>
    {{ block "main" . }}
        Welcome
    {{ end }}
</body>
</html>

Create layouts/index.html:

{{ define "main" }}
    
{{ end }}

Exercise: Change “Welcome” to “Hello World”.

Update layouts/_default/baseof.html:

Solution
<html>
<head>
    <title>My Site</title>
</head>
<body>
    {{ block "main" . }}
        Hello World
    {{ end }}
</body>
</html>

Exercise: Put “Welcome” in an <h1> header.

Update layouts/_default/baseof.html:

Solution
<html>
<head>
    <title>My Site</title>
</head>
<body>
    {{ block "main" . }}
        <h1>Welcome</h1>
    {{ end }}
</body>
</html>

Blocks in baseof.html define sections that other templates can override.

Example: Override base template with main content

Keep layouts/_default/baseof.html as is:

<html>
<head>
    <title>My Site</title>
</head>
<body>
    {{ block "main" . }}
        <h1>Welcome</h1>
    {{ end }}
</body>
</html>

Update layouts/index.html to override with different content:

{{ define "main" }}
    <p>This is the homepage content</p>
{{ end }}

Exercise: Add “Welcome to my site” as a second paragraph in index.html.

Update layouts/index.html:

Solution
{{ define "main" }}
    <p>This is the homepage content</p>
    <p>Welcome to my site</p>
{{ end }}

Exercise: Add the <h1>Welcome</h1> heading before the main block in baseof.html.

Update layouts/_default/baseof.html:

Solution
<html>
<head>
    <title>My Site</title>
</head>
<body>
    <h1>Welcome</h1>
    {{ block "main" . }}
        <p>Default content</p>
    {{ end }}
</body>
</html>

There can be multiple blocks.

Example: Add title block with title. The title should appear on the browser tab

Update layouts/_default/baseof.html:

<html>
<head>
    {{ block "title" . }}
        <title>My Portfolio</title>
    {{ end }}
</head>
<body>
    <h1>Welcome</h1>
    {{ block "main" . }}
        <p>Default content</p>
    {{ end }}
</body>
</html>

Exercise: Change the title to include both title and author: “My Portfolio - John Doe”.

Update layouts/_default/baseof.html:

Solution
<html>
<head>
    {{ block "title" . }}
        <title>My Portfolio - John Doe</title>
    {{ end }}
</head>
<body>
    <h1>Welcome</h1>
    {{ block "main" . }}
        <p>Default content</p>
    {{ end }}
</body>
</html>

Exercise: Replace the hardcoded title and author with .Site.Title and .Site.Params.author from hugo.toml.

Update layouts/_default/baseof.html:

Solution
<html>
<head>
    {{ block "title" . }}
        <title>{{ .Site.Title }} - {{ .Site.Params.author }}</title>
    {{ end }}
</head>
<body>
    <h1>Welcome</h1>
    {{ block "main" . }}
        <p>Default content</p>
    {{ end }}
</body>
</html>

Exercise: Add a footer block after the main block with copyright text “© 2025 All rights reserved”.

Update layouts/_default/baseof.html:

Solution
<html>
<head>
    {{ block "title" . }}
        <title>{{ .Site.Title }} - {{ .Site.Params.author }}</title>
    {{ end }}
</head>
<body>
    <h1>Welcome</h1>
    {{ block "main" . }}
        <p>Default content</p>
    {{ end }}
    {{ block "footer" . }}
        <p>© 2025 All rights reserved</p>
    {{ end }}
</body>
</html>

Single page templates also inherit from the base template. They are used for individual content pages.

Example: Create single.html that inherits from base template and verify that it works on about page

hugo new about/index.md

Create layouts/_default/single.html:

{{ define "main" }}
    {{ .Content }}
{{ end }}

Exercise: Override the title block in single.html to display “This is Single Layout”. Verify that it works on interests page

hugo new interests/index.md

Update layouts/_default/single.html:

Solution
{{ define "title" }}
    <title>This is Single Layout</title>
{{ end }}

{{ define "main" }}
    {{ .Content }}
{{ end }}

Exercise: Override the title block and the footer block in single.html and verify that it works on contact page.

hugo new contact/index.md
{{ define "title" }}
    <title>This is Single Layout</title>
{{ end }}

{{ define "main" }}
    {{ .Content }}
{{ end }}

{{ define "footer" }}
    <hr>
    <p>This is the single page footer</p>
{{ end }}

Section 2: Partials

Background

Partials are reusable template components. They are stored in layouts/partials/ and can be called from any template. Partials help avoid repeating code across multiple templates.

Exercises

This section covers creating partials and passing parameters to them.

Concept Description
layouts/partials/ Directory for partial files
{{ partial "name.html" . }} Call partial
{{ partial "name.html" "param" }} Call partial with parameter
{{ partial "name.html" (dict "key" "value") }} Pass multiple parameters

Simple partials contain HTML that can be reused in multiple templates. They are called with the {{ partial }} function.

Example: Make title block into a partial and call partial

Create layouts/partials/header.html:

<title>{{ .Site.Title }}</title>

Update layouts/_default/baseof.html to call the partial:

<html>
<head>
    {{ partial "header.html" . }}
</head>
<body>
    <h1>Welcome</h1>
    {{ block "main" . }}
        <p>Default content</p>
    {{ end }}
    {{ block "footer" . }}
        <p>© 2025 All rights reserved</p>
    {{ end }}
</body>
</html>

Exercise: Move the footer content into footer.html partial and call it in baseof.html.

Create layouts/partials/footer.html:

Solution
<p>© 2025 All rights reserved</p>

Update layouts/_default/baseof.html:

Solution
<html>
<head>
    {{ partial "header.html" . }}
</head>
<body>
    <h1>Welcome</h1>
    {{ block "main" . }}
        <p>Default content</p>
    {{ end }}
    {{ partial "footer.html" . }}
</body>
</html>

Exercise: Add GitHub and LinkedIn links to the footer partial.

Update layouts/partials/footer.html:

Solution
<p>© 2025 All rights reserved</p>
<p>
    <a href="https://github.com/username">GitHub</a> | 
    <a href="https://linkedin.com/in/username">LinkedIn</a>
</p>

Partials can accept parameters. Parameters are passed as the second argument to {{ partial }}.

Example: Accept one parameter for header partial and call partial

Update layouts/partials/header.html:

<title>{{ . }}</title>

Update layouts/_default/baseof.html to call with parameter:

<html>
<head>
    {{ partial "header.html" "My Custom Title" }}
</head>
<body>
    <h1>Welcome</h1>
    {{ block "main" . }}
        <p>Default content</p>
    {{ end }}
    {{ partial "footer.html" . }}
</body>
</html>

Exercise: Modify the footer partial to accept a year parameter and pass “2024” when calling it.

Update layouts/partials/footer.html:

Solution
<p>© {{ . }} All rights reserved</p>
<p>
    <a href="https://github.com/username">GitHub</a> | 
    <a href="https://linkedin.com/in/username">LinkedIn</a>
</p>

Update layouts/_default/baseof.html:

Solution
<html>
<head>
    {{ partial "header.html" "My Custom Title" }}
</head>
<body>
    <h1>Welcome</h1>
    {{ block "main" . }}
        <p>Default content</p>
    {{ end }}
    {{ partial "footer.html" "2024" }}
</body>
</html>

Exercise: Modify the footer partial to accept both year and author parameters using dict.

Update layouts/partials/footer.html:

Solution
<p>© {{ .year }} {{ .author }} - All rights reserved</p>
<p>
    <a href="https://github.com/username">GitHub</a> | 
    <a href="https://linkedin.com/in/username">LinkedIn</a>
</p>

Update layouts/_default/baseof.html:

Solution
<html>
<head>
    {{ partial "header.html" "My Custom Title" }}
</head>
<body>
    <h1>Welcome</h1>
    {{ block "main" . }}
        <p>Default content</p>
    {{ end }}
    {{ partial "footer.html" (dict "year" "2024" "author" "John Doe") }}
</body>
</html>

Navigation is a common partial used across multiple pages. It contains links to different sections of the site.

Example: Create nav partial

Create layouts/partials/nav.html:

<nav>
    <a href="/">Home</a> | 
    <a href="/about">About</a> | 
</nav>

Update layouts/_default/baseof.html:

<html>
<head>
    {{ partial "header.html" "My Custom Title" }}
</head>
<body>
    {{ partial "nav.html" . }}
    <h1>Welcome</h1>
    {{ block "main" . }}
        <p>Default content</p>
    {{ end }}
    {{ partial "footer.html" (dict "year" "2024" "author" "John Doe") }}
</body>
</html>

Exercise: Add Interests to the navigation in the partial

<nav>
    <a href="/">Home</a> | 
    <a href="/about">About</a> | 
    <a href="/interests">Intersts</a>
</nav>
<html>
<head>
    {{ partial "header.html" "My Custom Title" }}
</head>
<body>
    {{ partial "nav.html" . }}
    <h1>Welcome</h1>
    {{ block "main" . }}
        <p>Default content</p>
    {{ end }}
    {{ partial "footer.html" (dict "year" "2024" "author" "John Doe") }}
</body>
</html>

Exercise: Create contact.md and add a Contact link to the nav partial.

Create content/contact.md:

Solution
+++
title = "Contact"
+++

Contact information.

Update layouts/partials/nav.html:

Solution
<nav>
    <a href="/">Home</a> | 
    <a href="/about">About</a> | 
    <a href="/interests">Interests</a> | 
    <a href="/contact">Contact</a>
</nav>

Section 3: Lookup Order

Background

Hugo follows a specific order when looking for templates. More specific templates override general ones. For example, a template for a specific page overrides the default single template.

Exercises

This section covers creating page-specific templates and overriding blocks.

Concept Description
layouts/_default/single.html Default single page template
layouts/pagename.html Page-specific template
Template lookup order More specific beats general

Page-specific templates override the default single template. Hugo looks for templates matching the page name first.

Example: Create a separate template for about single page

Create about/single.html:

{{ define "main" }}
    <h2>About Page - Custom Layout</h2>
    {{ .Content }}
{{ end }}

Exercise: Create layouts/interests.html with a custom heading for the interests page.

Create layouts/interests.html:

Solution
{{ define "main" }}
    <h2>Interests Page - Custom Layout</h2>
    {{ .Content }}
{{ end }}

Exercise: Delete layouts/interests.html so the interests page uses the default single.html again.

Solution
 <!-- Delete layouts/interests.html -->
 <!-- Now interests page will use layouts/_default/single.html -->