16.5 Taxonomy Term Page: Rendering All Pages for a Term
Right, so you’ve got your terms set up and your content tagged. Now comes the fun part: actually showing off that curated list of content on the taxonomy term page. This is Hugo’s way of saying, “Hey, you asked for all the pages tagged ‘banana-bread’, here they are.” It’s a powerful concept, but the default template can feel a little… sparse. Let’s fix that.
The magic happens in a template file at /layouts/_default/taxonomy.html. If that doesn’t exist, Hugo falls back to its internal default, which is about as exciting as plain toast. We’re going to make banana bread toast.
The Core Loop: .Pages
Inside your taxonomy.html, the most important variable is .Pages. This is a collection of all the pages that have this specific term. Your job is to range over them and display what you need.
<!-- layouts/_default/taxonomy.html -->
<main>
<h1>Posts about {{ .Title }}</h1>
<p>Here you'll find all {{ len .Pages }} of my brilliant thoughts on {{ .Title }}.</p>
<ul>
{{ range .Pages }}
<li>
<article>
<h2><a href="{{ .RelPermalink }}">{{ .Title }}</a></h2>
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "January 2, 2006" }}</time>
<p>{{ .Summary }}</p>
</article>
</li>
{{ end }}
</ul>
</main>
Why .Title and not something like .Data.Term? Because the .Title of the taxonomy page is the term, nicely capitalized. Hugo’s doing you a solid. The .Data family ({.Data.Term}, {.Data.Plural}}) is still there if you need it, but .Title is cleaner for this purpose.
Pagination: Because Your “Recipes” Tag Might Blow the Stack
If you have a popular term with hundreds of pages, dumping them all into a single HTML page is a performance nightmare. We need pagination. This is where Hugo’s built-in paginator shines. We don’t use .Pages directly; we hand the collection off to the paginator.
<!-- layouts/_default/taxonomy.html -->
{{ $paginator := .Paginate .Pages }}
<main>
<h1>Posts about {{ .Title }}</h1>
{{ range $paginator.Pages }}
<!-- Your article rendering code from above -->
{{ end }}
<!-- This next part is non-negotiable -->
{{ template "_internal/pagination.html" . }}
</main>
The _internal/pagination.html template is Hugo’s gift to you—it generates sensible “Previous / Page 2 / Next” links. You can absolutely create a custom pagination.html template if you want it to match your site’s style, but for getting started, the internal one is a lifesaver.
The Summary Gotcha
Notice I used .Summary in the first example. It’s convenient, but it’s also a trap. Hugo generates the summary by either taking the first 70 words of your content or everything before a manually inserted `
tag. The problem? It's a raw string of HTML. If the first sentence includes an` tag, that tag will be opened but not necessarily closed within the 70-word limit, potentially breaking your entire layout.
The solution is to pipe it through Hugo’s plainify and safeHTML functions. It’s a bit of a dance:
<p>{{ .Summary | plainify | safeHTML }}</p>
This strips the HTML tags for the word calculation (plainify) and then says, “just output whatever HTML is left, I know what I’m doing” (safeHTML). It’s not perfect, but it’s the most robust way to handle it without writing a custom summary function.
Accessing the Taxonomy Context
Sometimes you need to know what kind of taxonomy you’re in. Is this a “tag” page or a “category” page? This is where we dive into the .Data object. It’s crucial for creating nuanced templates.
<main>
<h1>
{{ if eq .Data.Plural "tags" }}
All Posts Tagged
{{ else if eq .Data.Plural "categories" }}
All Posts in Category
{{ else }}
All Posts for
{{ end }}: "{{ .Title }}"
</h1>
<!-- Show a different description based on the taxonomy type -->
<p>
{{ if eq .Data.Plural "tags" }}
Viewing all content tagged with "{{ .Title }}". It's a niche, but someone's got to have it.
{{ else }}
Viewing all content in the "{{ .Title }}" category.
{{ end }}
</p>
</main>
This allows you to tailor the language and structure of the page based on whether the user is browsing tags, categories, or any other custom taxonomy you’ve invented. It’s this kind of detail that separates a thoughtful site from a generic one. Don’t just list things; provide context.