Objective
Render something like this, from a base template, for each pager of a paginated collection:
<head>
...
<link rel="prev" href="http://localhost:1313/books/page/2/">
<link rel="next" href="http://localhost:1313/books/page/4/">
<link rel="canonical" href="http://localhost:1313/books/page/3/">
<title>Books - Page 3 | My Site</title>
...
</head>
Challenge
To get the URL of the next, previous, and current pager you must call the Paginator
method on a Page
object, but that also invokes pagination. The result of pagination is cached, so you can’t subsequently change (filter, sort, or group) the page collection in a list template (home, section, taxonomy, or term).
Our goal is to define the page collection and pager size (number of pages per pager) within a list template, but access the paginator from a base template before rendering the list. This sounds paradoxical (chicken/egg), but is easy to implement using Hugo’s base/block construct.
Approach
There are three pieces to this approach:
- The base template
- The list template
- The partial that renders the
link
elements within thehead
element
layouts/_default/baseof.html
<head>
...
{{ block "pagination" . }}{{ end }}
{{ partial "head/link-and-title-elements.html" . }}
...
</head>
layouts/posts/list.html
{{ define "pagination" }}
{{ $pages := .Pages.ByTitle }}
{{ .Store.Set "paginator" (.Paginate $pages 5) }}
{{ end }}
{{ define "main" }}
<h1>{{ .Title }}</h1>
{{ .Content }}
{{ with .Store.Get "paginator" }}
{{ range .Pages }}
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
{{ end }}
{{ template "_internal/pagination.html" $ }}
{{ else }}
{{ range .Pages }}
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
{{ end }}
{{ end }}
{{ end }}
If you’re not going to paginate a given list template, do not define a “pagination” block in the above, or leave its contents empty.
layouts/partials/head/link-and-title-elements.html
{{- $canonicalURL := .Permalink }}
{{- $title := printf "%s | %s" .Title site.Title }}
{{- if .IsHome }}
{{- $title = site.Title }}
{{- end }}
{{- with .Store.Get "paginator" }}
{{- $canonicalURL = .URL | absLangURL }}
{{- if gt .TotalPages 1 }}
{{- $title = printf "%s - Page %d | %s" $.Title .PageNumber site.Title }}
{{- if $.IsHome }}
{{- $title = printf "%s - Page %d" site.Title .PageNumber }}
{{- end }}
{{- end }}
{{- with .Prev }}
<link rel="prev" href="{{ .URL | absLangURL }}">
{{- end }}
{{- with .Next }}
<link rel="next" href="{{ .URL | absLangURL }}">
{{- end }}
{{- end }}
<link rel="canonical" href="{{ $canonicalURL }}">
<title>{{- $title }}</title>
Example
This example contains three list templates: one for a “books” section, one for a “films” section, and one for a “sculptures” section. Two of the list pages are paginated, but each with different settings. The other list page is not paginated.
git clone --single-branch -b hugo-forum-topic-50340 https://github.com/jmooring/hugo-testing hugo-forum-topic-50340
cd hugo-forum-topic-50340
hugo server
Related
This approach addresses this (somewhat) related issue:
https://github.com/gohugoio/hugo/issues/4507