Pagination
Displaying a large page collection on a list page is not user-friendly:
- A massive list can be intimidating and difficult to navigate. Users may get lost in the sheer volume of information.
- Large pages take longer to load, which can frustrate users and lead to them abandoning the site.
- Without any filtering or organization, finding a specific item becomes a tedious scrolling exercise.
Improve usability by paginating home
, section
, taxonomy
, and term
pages.
Terminology
- paginate
- To split a list page into two or more subsets.
- pagination
- The process of paginating a list page.
- pager
- Created during pagination, a pager contains a subset of a list page and navigation links to other pagers.
- paginator
- A collection of pagers.
Configuration
Control pagination behavior in your site configuration. These are the default settings:
pagination:
disableAliases: false
pagerSize: 10
path: page
[pagination]
disableAliases = false
pagerSize = 10
path = 'page'
{
"pagination": {
"disableAliases": false,
"pagerSize": 10,
"path": "page"
}
}
- disableAliases
- (
bool
) Whether to disable alias generation for the first pager. Default isfalse
. - pagerSize
- (
int
) The number of pages per pager. Default is10
. - path
- (
string
) The segment of each pager URL indicating that the target page is a pager. Default ispage
.
With multilingual sites you can define the pagination behavior for each language:
languages:
de:
contentDir: content/de
languageCode: de-DE
languageDirection: ltr
languageName: Deutsch
pagination:
disableAliases: true
pagerSize: 20
path: blatt
weight: 2
en:
contentDir: content/en
languageCode: en-US
languageDirection: ltr
languageName: English
pagination:
disableAliases: true
pagerSize: 10
path: page
weight: 1
[languages]
[languages.de]
contentDir = 'content/de'
languageCode = 'de-DE'
languageDirection = 'ltr'
languageName = 'Deutsch'
weight = 2
[languages.de.pagination]
disableAliases = true
pagerSize = 20
path = 'blatt'
[languages.en]
contentDir = 'content/en'
languageCode = 'en-US'
languageDirection = 'ltr'
languageName = 'English'
weight = 1
[languages.en.pagination]
disableAliases = true
pagerSize = 10
path = 'page'
{
"languages": {
"de": {
"contentDir": "content/de",
"languageCode": "de-DE",
"languageDirection": "ltr",
"languageName": "Deutsch",
"pagination": {
"disableAliases": true,
"pagerSize": 20,
"path": "blatt"
},
"weight": 2
},
"en": {
"contentDir": "content/en",
"languageCode": "en-US",
"languageDirection": "ltr",
"languageName": "English",
"pagination": {
"disableAliases": true,
"pagerSize": 10,
"path": "page"
},
"weight": 1
}
}
}
Methods
To paginate a home
, section
, taxonomy
, or term
page, invoke either of these methods on the Page
object in the corresponding template:
The Paginate
method is more flexible, allowing you to:
- Paginate any page collection
- Filter, sort, and group the page collection
- Override the number of pages per pager as defined in your site configuration
By comparison, the Paginator
method paginates the page collection passed into the template, and you cannot override the number of pages per pager.
Examples
To paginate a list page using the Paginate
method:
{{ $pages := where site.RegularPages "Type" "posts" }}
{{ $paginator := .Paginate $pages.ByTitle 7 }}
{{ range $paginator.Pages }}
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
{{ end }}
{{ template "_internal/pagination.html" . }}
In the example above, we:
- Build a page collection
- Sort the page collection by title
- Paginate the page collection, with 7 pages per pager
- Range over the paginated page collection, rendering a link to each page
- Call the embedded pagination template to create navigation links between pagers
To paginate a list page using the Paginator
method:
{{ range .Paginator.Pages }}
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
{{ end }}
{{ template "_internal/pagination.html" . }}
In the example above, we:
- Paginate the page collection passed into the template, with the default number of pages per pager
- Range over the paginated page collection, rendering a link to each page
- Call the embedded pagination template to create navigation links between pagers
Caching
Regardless of pagination method, the initial invocation is cached and cannot be changed. If you invoke pagination more than once for a given list page, subsequent invocations use the cached result. This means that subsequent invocations will not behave as written.
When paginating conditionally, do not use the compare.Conditional
function due to its eager evaluation of arguments. Use an if-else
construct instead.
Grouping
Use pagination with any of the grouping methods. For example:
{{ $pages := where site.RegularPages "Type" "posts" }}
{{ $paginator := .Paginate ($pages.GroupByDate "Jan 2006") }}
{{ range $paginator.PageGroups }}
<h2>{{ .Key }}</h2>
{{ range .Pages }}
<h3><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h3>
{{ end }}
{{ end }}
{{ template "_internal/pagination.html" . }}
Navigation
As shown in the examples above, the easiest way to add navigation between pagers is with Hugo’s embedded pagination template:
{{ template "_internal/pagination.html" . }}
The embedded pagination template has two formats: default
and terse
. The above is equivalent to:
{{ template "_internal/pagination.html" (dict "page" . "format" "default") }}
The terse
format has fewer controls and page slots, consuming less space when styled as a horizontal list. To use the terse
format:
{{ template "_internal/pagination.html" (dict "page" . "format" "terse") }}
Create custom navigation components using any of the Pager
methods:
- First
- Returns the first pager in the pager collection.
- HasNext
- Reports whether there is a pager after the current pager.
- HasPrev
- Reports whether there is a pager before the current pager.
- Last
- Returns the last pager in the pager collection.
- Next
- Returns the next pager in the pager collection.
- NumberOfElements
- Returns the number of pages in the current pager.
- PageGroups
- Returns the page groups in the current pager.
- PageNumber
- Returns the current pager’s number within the pager collection.
- Pagers
- Returns the pagers collection.
- PagerSize
- Returns the number of pages per pager.
- Pages
- Returns the pages in the current pager.
- PageSize
- Returns the number of pages per pager.
- Prev
- Returns the previous pager in the pager collection.
- TotalNumberOfElements
- Returns the number of pages in the pager collection.
- TotalPages
- Returns the number of pagers in the pager collection.
- URL
- Returns the URL of the current pager relative to the site root.
Structure
The example below depicts the published site structure when paginating a list page.
With this content:
content/
├── posts/
│ ├── _index.md
│ ├── post-1.md
│ ├── post-2.md
│ ├── post-3.md
│ └── post-4.md
└── _index.md
And this site configuration:
pagination:
disableAliases: false
pagerSize: 2
path: page
[pagination]
disableAliases = false
pagerSize = 2
path = 'page'
{
"pagination": {
"disableAliases": false,
"pagerSize": 2,
"path": "page"
}
}
And this section template:
{{ range (.Paginate .Pages).Pages }}
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
{{ end }}
{{ template "_internal/pagination.html" . }}
The published site has this structure:
public/
├── posts/
│ ├── page/
│ │ ├── 1/
│ │ │ └── index.html <-- alias to public/posts/index.html
│ │ └── 2/
│ │ └── index.html
│ ├── post-1/
│ │ └── index.html
│ ├── post-2/
│ │ └── index.html
│ ├── post-3/
│ │ └── index.html
│ ├── post-4/
│ │ └── index.html
│ └── index.html
└── index.html
To disable alias generation for the first pager, change your site configuration:
pagination:
disableAliases: true
pagerSize: 2
path: page
[pagination]
disableAliases = true
pagerSize = 2
path = 'page'
{
"pagination": {
"disableAliases": true,
"pagerSize": 2,
"path": "page"
}
}
Now the published site will have this structure:
public/
├── posts/
│ ├── page/
│ │ └── 2/
│ │ └── index.html
│ ├── post-1/
│ │ └── index.html
│ ├── post-2/
│ │ └── index.html
│ ├── post-3/
│ │ └── index.html
│ ├── post-4/
│ │ └── index.html
│ └── index.html
└── index.html