07 – Minimizing duplication with the base template and partials
So far we’ve created two template files: list.html
and single.html
. Even though we’ve got only a couple templates, if you’re an experienced web developer you’ve probably already spotted a problem.
Both templates contain boilerplate code that is common to any HTML document, such as the doctype declaration and <head>
section with stylesheet references.
Duplicating this code in every template file results in a site that is difficult to maintain and prone to errors. Instead, we want to organize our code in a way that promotes reuse, avoids duplication, and makes our code easier to understand.
Fortunately, Hugo offers a couple features that help us to organize our code more effectively. In this section, we’ll take a close look at two important features: the base template and partial templates.
The base template
In Hugo, you can declare a single template that other templates can inherit from. This is called the base template, and it has a filename of baseof.html
.
We’ll add a base template to our pets website to eliminate some of our duplicate code.
In the _default
directory, create a file called baseof.html
:
vim layouts/_default/baseof.html
And then add the following code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css">
<style>
header {
background-color: #333;
color:white;
margin:1rem 0;
padding: 1rem;
}
</style>
</head>
<body>
<main class="container">
{{ block "main" . }}
{{ end }}
</main>
</body>
</html>
You can see that this code looks a lot like the other templates we’ve created so far, with the exception of the code block with the block
command.
In this template, block
defines a location into which a named block of code from another template is inserted. block
takes two arguments: the label of the code that will be inserted, and the context. Context is a more complicated topic that we’ll discuss in a future article. For this example, the context is the current page, which is referenced using .
, also referred to as a dot.
This block command will cause Hugo to search for a code block labelled main
as it follows the rest of its template lookup order. Let’s update our single.html
file to see this in action. Replace the entire contents of your single.html
file with this:
|
|
When your site has a base template, the existing lookup rules still apply. For example, if we navigate to our Beagles page, Hugo will start constructing the layout by looking for a baseof.html
file. If it finds one, it will use the code for the page layout.
Next, Hugo will follow its lookup rules and look for a single.html
file. If it finds one and it contains a complete template, that template would take precedence and replace the code from the base template. Hugo continues doing this for its complete lookup order, which is very lengthy and complicated.
However, because our updated single.html
doesn’t contain a complete template but a code block labelled main
, it will insert that block into our baseof.html
layout where we placed the {{ block "main" . }}{{ end }}
code.
Because single.html
is the last file that Hugo finds in its lookup order, it stops and constructs the final layout.
Now, if you navigate to http://localhost:1313/dogs/beagle/, you should find that the page looks exactly the same as it did before, even though we’ve made some significant changes to our template files.
Let’s update our list template in a similar way. Open the list.html
file and replace the contents with this:
{{ define "main" }}
<header>
List template : {{ .Kind }}
</header>
<ul>
{{ range .Paginator.Pages }}
<li><a href="{{.RelPermalink}}">{{.Title}}</a></li>
{{ end }}
</ul>
{{ template "_internal/pagination.html" . }}
{{ end }}
Now when you navigate to http://localhost:1313/dogs/, you should find that our list page is unchanged as well.
While the web page looks the same as it did before, you can see that we’ve made a big improvement to our code by eliminating a lot of duplication.
While our trivial pets website has only list and single layouts so far, it’s not hard to imagine that with a real website, you would have a lot of code duplication without the ability to reuse code in this way.
One other thing to note is that our example contains only a single named code block, but templates can have multiple blocks as long as you give them different names. For example, in addition to our main block, we could add “sidebar” and “navigation” blocks.
Partial templates
Partials are another Hugo feature that promotes code reuse. In the same way that we included a defined code block from our single page template into our base template, partial templates are snippets of code that can be included into another.
Let’s jump right into an example. Right now the <head>
section of our baseof.html
looks like this:
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css">
<style>
header {
background-color: #333;
color:white;
margin:1rem 0;
padding: 1rem;
}
</style>
</head>
Open this file and change it to this:
<head>
<meta charset="UTF-8">
{{ partial "styles.html" . }}
</head>
By convention, Hugo will look for partial files in layouts/partials
. This directory doesn’t exist yet in our example project, so go ahead and create it:
mkdir layouts/partials
Now create a file:
vim layouts/partials/styles.html
… and then add the code that we removed earlier from our base template:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css">
<style>
header {
background-color: #333;
color:white;
margin:1rem 0;
padding: 1rem;
}
</style>
Now navigate to any page of the site, like the Beagles page. If you did everything correctly, the website should look the same, and the <head>
should still contain the link and style tags. You can view source in your browser to double check.
Base templates with named code blocks, and partials: which to use?
In this chapter, we’ve learned how to build page layouts using code blocks and partials. Both are methods of including code from one template into another, and can help you organize your code better and eliminate duplication. So which should you use?
In general, partials are useful for snippets of code that are used across your project, such as page headers and footers, meta tags, styles, and Javascript. Partials can also contain not just HTML but Hugo functions, which means you can use partials for page components such as a tag cloud, a list of popular articles, or a search widget. We’ll tackle that in a future chapter.
Named code blocks and base templates also make it easier to reuse code across your website. Unlike partials which generally should be used for fragments of code that can be reused anywhere in your website, you’ll want to use code blocks to minimize code duplication that result from Hugo’s page lookup order.
For example, in our pets website, our List and Single layouts originally had the same HTML boilerplate. We moved that boilerpoint code into a base template and used code blocks to ensure that the template files for those pages contained only the code that is unique to those pages.