Pagination: using pager URLs within link elements

Objective

Render something like this, from a base template, for each pager of a paginated collection:

<head>
  ...
  <link rel="next" href="/books/page/4/">
  <link rel="prev" href="/books/page/2/">
  <link rel="canonical" href="https://example.org/books/page/3/">
  ...
</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:

  1. The base template
  2. The list template
  3. The partial that renders the link elements within the head element
layouts/_default/baseof.html
<head>
  ...
  {{ block "pagination" . }}{{ end }}
  {{ partial "links-next-previous-canonical.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/links-next-previous-canonical.html
{{ $href := .Permalink }}
{{ with .Store.Get "paginator" }}
  {{ $href = .URL | absURL }}
  {{ with .Next }}
    <link rel="next" href="{{ .URL }}">
  {{ end }}
  {{ with .Prev }}
    <link rel="prev" href="{{ .URL }}">
  {{ end }}
{{ end }}
<link rel="canonical" href="{{ $href }}">

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

2 Likes