06 – Listing your content

Last Updated: Mar 31, 2022

In this lesson we’ll learn the how to create templates to list the content in our site.

Before we get started, let’s add another piece of content to our dogs section so that we have a least a couple pages to show in our list. Again, you can create a new content page with the hugo new command:

hugo new dogs/beagle/index.md

… and then add some content to the file. (Your automatically-generated date stamp will be different from mine.)

---
title: "Beagle"
date: 2022-04-06T13:53:38-04:00
draft: false
---
A Beagle is a small hound, often with black, brown, and white coloring.

Creating a basic layout to list our site’s content

The previous lessons used the layout/_default/single.html template to display a single page of content.

Create a file layout/_default/list.html with 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">
		<header>
			List template : {{ .Kind }}
		</header>
		<ul>
			{{ range .Pages }}
				<li><a href="{{.RelPermalink}}">{{.Title}}</a></li>
			{{ end }}
		</ul>
	</main>
</body>

</html>

When you’re done, load the home page of your site at http://localhost:1313 and you should see a page like this:

example

We have a simple Hugo code block inside our HTML <ul> tag, but it does introduce some new concepts. Let’s take a look at them in-depth.

The range function

Inside our code block the first keyword is range. It’s a Hugo function, which means that it performs an operation on the input that we supply to it. You can get a full list of Hugo functions on gohugo.io.

The range function, according to the documentation, iterates over a map, array, or slice. Maps, arrays, and slices are types of collections in golang.

The .Pages collection

According to the documentation, .range accepts a single parameter. In this example, we are passing the .Pages collection as a parameter to our range function. The official documentation refers to .Pages simply as a variable, but to be more specific it is a collection that we can iterate over using range. You can read more about .Pages at gohugo.io.

You might be familar with for-each loops in other programming languages. You can think of range as for-each loop over .Pages, which is a collection of Page variables.

The next line of our template outputs the page title of each Page in the collection and a link to that page. When you load this page in a browser and click on any of the items, you will navigate to the URL of that page.

.Title and .RelPermalink are variables that are available within each Page. The title and some other values, such as the date, can be set in front matter. Other variables are generated by Hugo and provide useful features, such as .TableOfContents.

You can see a full list of Page variables at the official documentation.

Kinds of Pages

The last thing to mention in our list layout is the .Kind variable. It lists the kind of the current page. These can include page, home, section, taxonomy, and term. On the home page, you can see that our layout displays “List template : home” in the header.

In the examples that follow, you’ll see how the .Kind variable outputs different values for the different kinds of page in our site.

More about the Pages variable

You’ll notice in our example that the home page of our site doesn’t list all of the pages in our site. Instead, it lists the dogs and cats sections. This is because a section in Hugo is a kind of page, and the .Pages variable only lists the first level of pages under the current list page.

For example, let’s click on the dogs link.

example

You can see that “List template : section” is now displayed in the header and all of the top-level pages in the dogs section are now listed.

What if you wanted your home page to list all regular pages within our website - in other words, all pages with a Kind of page?

You can do that by ranging over the .Site.RegularPages variable like this:

1
{{ range .Site.RegularPages }}

That will cause our list layout to display all regular pages in our site, regardless of hierarchy:

example

There are several page-related variables you can use, depending on which collection of pages you want to display. Again, refer to the official documentation for details.

Pagination

If you have more than a dozen or so pages in your website, you’ll probably want to paginate your list pages to make it easier for users to navigate forward, back, or jump to an arbitrary group of pages.

Building that yourself is complicated, but fortunately Hugo provides built-in functionality with the .Paginator function.

The Paginator generates list pages with a configurable count. It also creates forward, back, first, and last links.

To use it in our list layout, you range over the .Paginator.Pages variable:

1
{{ range .Paginator.Pages }}

This Pages collection contains the list of pages according to the current position in the collection and the configured number of pages to display on each list page. By default, the Paginator will display 10 pages per list page.

To display the pagination links, simply drop this code snippet into your layout:

1
{{ template "_internal/pagination.html" . }}

Note that if you’re following along with our example, the pagination HTML won’t show up on the page because we have fewer than 10 articles.

Unlike the template files we’ve created so far, this is an *internal template.. This means that the code for it is provided out-of-the-box by Hugo: it doesn’t need to be a file in our layouts directory.

Internal templates will work well for many projects without modification, but you can override them with your own code if you need customization. Hugo comes out-of-the-box with several internal templates. You can learn more later in our section on internal templates.

The Paginator has several more features that you can learn about in the official docs.