- Stable
2.0.1
Toggle Menu
1.93s
29.05s
Pagination
Contents
- Paging an Array
- Creating Navigation Links to your Pages
- Paging an Object
- Paginate a global or local data file
- Remapping with permalinks
- Aliasing to a different variable
- Paging a Collection
- Generating an Empty Results Page
- Modifying the Data Set prior to Pagination
- Add All Pagination Pages to Collections
- Full Pagination Option List
- Related
- From the Community
Pagination allows you to iterate over a data set and create multiple files from a single template. The input data can be in the form of an array or object defined in your frontmatter or in global data, or you can paginate a collection to make an easily digestible list of your posts.
Paging an Array
To iterate over a data set and create pages for individual chunks of data, use pagination. Enable in your templateβs front matter by adding the pagination
key.
Consider the following template, which will result in two pages being created, each of which will display two items from testdata
:
---
pagination:
data: testdata
size: 2
testdata:
- item1
- item2
- item3
- item4
---
<ol>
{%- for item in pagination.items %}
<li>{{ item }}</li>
{% endfor -%}
</ol>
If the above file were named paged.liquid
, it would create two pages in your output folder: _site/paged/index.html
and _site/paged/1/index.html
. These output paths are configurable with permalink
(see below).
---
pagination:
data: testdata
size: 2
testdata:
- item1
- item2
- item3
- item4
---
<ol>
{%- for item in pagination.items %}
<li>{{ item }}</li>
{% endfor -%}
</ol>
If the above file were named paged.njk
, it would create two pages in your output folder: _site/paged/index.html
and _site/paged/1/index.html
. These output paths are configurable with permalink
(see below).
export const data = {
pagination: {
data: "testdata",
size: 2
},
testdata: [
"item1",
"item2",
"item3",
"item4"
]
};
export function render(data) {
return `<ol>
${data.pagination.items.map(function(item) {
return `<li>${item}</li>`;
}).join("")
}
</ol>`;
};
If the above file were named paged.11ty.js
, it would create two pages in your output folder: _site/paged/index.html
and _site/paged/1/index.html
. These output paths are configurable with permalink
(see below).
exports.data = {
pagination: {
data: "testdata",
size: 2
},
testdata: [
"item1",
"item2",
"item3",
"item4"
]
};
exports.render = function(data) {
return `<ol>
${data.pagination.items.map(function(item) {
return `<li>${item}</li>`;
}).join("")
}
</ol>`;
};
If the above file were named paged.11ty.js
, it would create two pages in your output folder: _site/paged/index.html
and _site/paged/1/index.html
. These output paths are configurable with permalink
(see below).
We enable pagination and then give it a dataset with the data
key. We control the number of items in each chunk with size
. The pagination data variable will be populated with what you need to create each template. Hereβs whatβs in pagination
:
{
items: [], // Array of current pageβs chunk of data
pageNumber: 0, // current page number, 0 indexed
// Cool URLs
hrefs: [], // Array of all page hrefs (in order)
href: {
next: "β¦", // put inside <a href="">Next Page</a>
previous: "β¦", // put inside <a href="">Previous Page</a>
first: "β¦",
last: "β¦",
},
pages: [], // Array of all chunks of paginated data (in order)
page: {
next: {}, // Next pageβs chunk of data
previous: {}, // Previous pageβs chunk of data
first: {},
last: {},
}
}
Expand to see all of the extra stuff in the pagination
object that you probably donβt need any more but itβs still in there for backwards compatibility.
In addition to the pagination
object entries documented above, it also has:
{
data: "β¦", // the original string key to the dataset
size: 1, // page chunk sizes
// Cool URLs
// Use pagination.href.next, pagination.href.previous, et al instead.
nextPageHref: "β¦", // put inside <a href="">Next Page</a>
previousPageHref: "β¦", // put inside <a href="">Previous Page</a>
firstPageHref: "β¦",
lastPageHref: "β¦",
// Uncool URLs
// These include index.html file names, use `hrefs` instead
links: [], // Array of all page links (in order)
// Deprecated things:
// nextPageLink
// previousPageLink
// firstPageLink
// lastPageLink
// pageLinks (alias to `links`)
}
Creating Navigation Links to your Pages
Learn how to create a list of links to every paginated page on a pagination template with a full Pagination Navigation tutorial.
Paging an Object
All of the examples thus far have paged Array data. Eleventy does allow paging objects too. Objects are resolved to pagination arrays using either the Object.keys
or Object.values
JavaScript functions. Consider the following templates:
---
pagination:
data: testdata
size: 1
testdata:
itemkey1: itemvalue1
itemkey2: itemvalue2
itemkey3: itemvalue3
---
<ol>
{%- for item in pagination.items %}
<li>{{ item }}={{testdata[item] }}</li>
{% endfor -%}
</ol>
---
pagination:
data: testdata
size: 1
testdata:
itemkey1: itemvalue1
itemkey2: itemvalue2
itemkey3: itemvalue3
---
<ol>
{%- for item in pagination.items %}
<li>{{ item }}={{testdata[item] }}</li>
{% endfor -%}
</ol>
export const data = {
pagination: {
data: "testdata",
size: 1,
},
testdata: {
itemkey1: "itemvalue1",
itemkey2: "itemvalue2",
itemkey3: "itemvalue3",
},
};
export function render(data) {
return `<ol>
${data.pagination.items
.map(function (item) {
return `<li>${(item = data.testdata[item])}</li>`;
})
.join("")}
</ol>`;
};
exports.data = {
pagination: {
data: "testdata",
size: 1,
},
testdata: {
itemkey1: "itemvalue1",
itemkey2: "itemvalue2",
itemkey3: "itemvalue3",
},
};
exports.render = function (data) {
return `<ol>
${data.pagination.items
.map(function (item) {
return `<li>${(item = data.testdata[item])}</li>`;
})
.join("")}
</ol>`;
};
In this example, we would get 3 pages that each print a key/value pair from testdata
. The paged items hold the object keys:
[
["itemkey1"], // pagination.items[0] holds the object key
["itemkey2"],
["itemkey3"],
];
You can use these keys to get access to the original value: testdata[ pagination.items[0] ]
.
If youβd like the pagination to iterate over the values instead of the keys (using Object.values
instead of Object.keys
), add resolve: values
to your pagination
front matter:
---
pagination:
data: testdata
size: 1
resolve: values
testdata:
itemkey1: itemvalue1
itemkey2: itemvalue2
itemkey3: itemvalue3
---
This resolves to:
[
["itemvalue1"], // pagination.items[0] holds the object value
["itemvalue2"],
["itemvalue3"],
];
Paginate a global or local data file
Read more about Template Data Files. The only change here is that you point your data
pagination key to the global or local data instead of data in the front matter. For example, consider the following globalDataSet.json
file in your global data directory.
{
"myData": ["item1", "item2", "item3", "item4"]
}
Your front matter would look like this:
---
pagination:
data: globalDataSet.myData
size: 1
---
<ol>
{%- for item in pagination.items %}
<li>{{ item }}</li>
{% endfor -%}
</ol>
---
pagination:
data: globalDataSet.myData
size: 1
---
<ol>
{%- for item in pagination.items %}
<li>{{ item }}</li>
{% endfor -%}
</ol>
export const data = {
pagination: {
data: "globalDataSet.myData",
size: 1,
},
};
export function render(data) {
return `<ol>
${data.pagination.items
.map(function (item) {
return `<li>${item}</li>`;
})
.join("")}
</ol>`;
};
exports.data = {
pagination: {
data: "globalDataSet.myData",
size: 1,
},
};
exports.render = function (data) {
return `<ol>
${data.pagination.items
.map(function (item) {
return `<li>${item}</li>`;
})
.join("")}
</ol>`;
};
Remapping with permalinks
Normally, front matter does not support template syntax, but permalink
does, enabling parametric URLs via pagination variables. Hereβs an example of a permalink using the pagination page number:
---
permalink: "different/page-{{ pagination.pageNumber }}/index.html"
---
Writes to _site/different/page-0/index.html
, _site/different/page-1/index.html
, et cetera.
That means Nunjucks will also let you start your page numbers with 1 instead of 0, by just adding 1 here:
---
permalink: "different/page-{{ pagination.pageNumber + 1 }}/index.html"
---
Writes to _site/different/page-1/index.html
, _site/different/page-2/index.html
, et cetera.
You can even use template logic here too:
---
permalink: "different/{% if pagination.pageNumber > 0 %}page-{{ pagination.pageNumber + 1 }}/{% endif %}index.html"
---
Writes to _site/different/index.html
, _site/different/page-2/index.html
, et cetera.
{{ pagination.pageNumber + 1 }}
is not supported in Liquid. Use {{ pagination.pageNumber | plus: 1 }}
instead.Use page item data in the permalink
You can do more advanced things like this:
---
pagination:
data: testdata
size: 1
testdata:
- My Item
permalink: "different/{{ pagination.items[0] | slugify }}/index.html"
---
Using a universal slug
filter (transforms My Item
to my-item
), this outputs: _site/different/my-item/index.html
.
Aliasing to a different variable
Ok, so pagination.items[0]
is ugly. We provide an option to alias this to something different.
---
pagination:
data: testdata
size: 1
alias: wonder
testdata:
- Item1
- Item2
permalink: "different/{{ wonder | slugify }}/index.html"
---
You can use the alias in your content too {{ wonder }}.
---
pagination:
data: testdata
size: 1
alias: wonder
testdata:
- Item1
- Item2
permalink: "different/{{ wonder | slugify }}/index.html"
---
You can use the alias in your content too {{ wonder }}.
export const data = {
pagination: {
data: "testdata",
size: 1,
alias: "wonder",
},
testdata: ["Item1", "Item2"],
permalink: function (data) {
return `different/${this.slugify(data.wonder)}/index.html`;
},
};
export function render(data) {
return `You can use the alias in your content too ${data.wonder}.`;
};
exports.data = {
pagination: {
data: "testdata",
size: 1,
alias: "wonder",
},
testdata: ["Item1", "Item2"],
permalink: function (data) {
return `different/${this.slugify(data.wonder)}/index.html`;
},
};
exports.render = function (data) {
return `You can use the alias in your content too ${data.wonder}.`;
};
This writes to _site/different/item1/index.html
and _site/different/item2/index.html
.
page
is a reserved word so you cannot use alias: page
. Read about Eleventyβs reserved data names in Eleventy Supplied Data.If your chunk size
is greater than 1, the alias will be an array instead of a single value.
---
pagination:
data: testdata
size: 2
alias: wonder
testdata:
- Item1
- Item2
- Item3
- Item4
permalink: "different/{{ wonder[0] | slugify }}/index.html"
---
You can use the alias in your content too {{ wonder[0] }}.
---
pagination:
data: testdata
size: 2
alias: wonder
testdata:
- Item1
- Item2
- Item3
- Item4
permalink: "different/{{ wonder[0] | slugify }}/index.html"
---
You can use the alias in your content too {{ wonder[0] }}.
export const data = {
pagination: {
data: "testdata",
size: 2,
alias: "wonder"
},
testdata: [
"Item1",
"Item2",
"Item3",
"Item4"
],
permalink: {
function(data) {
return `different/${this.slugify(data.wonder[0])}/index.html`
};
}
};
export function render(data) {
return `You can use the alias in your content too ${data.wonder[0]}.`;
}
exports.data = {
pagination: {
data: "testdata",
size: 2,
alias: "wonder"
},
testdata: [
"Item1",
"Item2",
"Item3",
"Item4"
],
permalink: {
function(data) {
return `different/${this.slugify(data.wonder[0])}/index.html`
};
}
};
exports.render = function (data) {
return `You can use the alias in your content too ${data.wonder[0]}.`;
}
This writes to _site/different/item1/index.html
and _site/different/item3/index.html
.
Paging a Collection
If youβd like to make a paginated list of all of your blog posts (any content with the tag post
on it), use something like the following template to iterate over a specific collection:
---
title: My Posts
pagination:
data: collections.post
size: 6
alias: posts
---
<ol>
{% for post in posts %}
<li><a href="{{ post.url }}">{{ post.data.title }}</a></li>
{% endfor %}
</ol>
---
title: My Posts
pagination:
data: collections.post
size: 6
alias: posts
---
<ol>
{% for post in posts %}
<li><a href="{{ post.url }}">{{ post.data.title }}</a></li>
{% endfor %}
</ol>
export const data = {
title: "My Posts",
pagination: {
data: "collections.post",
size: 6,
alias: "posts",
},
};
export function render(data) {
return `<ol>
${data.posts
.map(function (post) {
return `<li><a href="${post.url}">${post.title}</a></li>`;
})
.join("")}
</ol>`;
};
exports.data = {
title: "My Posts",
pagination: {
data: "collections.post",
size: 6,
alias: "posts",
},
};
exports.render = function (data) {
return `<ol>
${data.posts
.map(function (post) {
return `<li><a href="${post.url}">${post.title}</a></li>`;
})
.join("")}
</ol>`;
};
The above generates a list of links but you could do a lot more. See whatβs available in the Collection documentation (specifically templateContent
). If youβd like to use this to automatically generate Tag pages for your content, please read Quick Tip #004βCreate Tag Pages for your Blog.
Generating an Empty Results Page
Added in v2.0.0
By default, if the specified data set is empty, Eleventy will not render any pages. Use generatePageOnEmptyData: true
to generate one pagination output with an empty chunk []
of items.
---
title: Available Products
pagination:
data: collections.available
size: 6
generatePageOnEmptyData: true
---
Modifying the Data Set prior to Pagination
Reverse the Data
Use reverse: true
.
---
pagination:
data: testdata
size: 2
reverse: true
testdata:
- item1
- item2
- item3
- item4
---
Paginates to:
[
["item4", "item3"],
["item2", "item1"],
];
(More discussion at Issue #194)
As an aside, this could also be achieved in a more verbose way using the Collection API. This could also be done using the new before
callback .
Filtering Values
Use the filter
pagination property to remove values from paginated data.
---
pagination:
data: testdata
size: 1
filter:
- item3
testdata:
item1: itemvalue1
item2: itemvalue2
item3: itemvalue3
---
Paginates to:
[["item1"], ["item2"]];
This will work the same with paginated arrays or with resolve: values
for paginated objects.
---
pagination:
data: testdata
size: 1
resolve: values
filter:
- itemvalue3
testdata:
item1: itemvalue1
item2: itemvalue2
item3: itemvalue3
---
Paginates to:
[["itemvalue1"], ["itemvalue2"]];
The before
Callback
The most powerful tool to change the data. Use this callback to modify, filter, or otherwise change the pagination data however you see fit before pagination occurs.
---js
{
pagination: {
data: "testdata",
size: 2,
before: function(paginationData, fullData) {
// `fullData` is new in v1.0.1 and contains the full Data Cascade thus far
return paginationData.map(entry => `${entry} with a suffix`);
}
},
testdata: [
"item1",
"item2",
"item3",
"item4"
]
}
---
<!-- the rest of the template -->
The above will iterate over a data set containing: ["item1 with a suffix", "item2 with a suffix", "item3 with a suffix", "item4 with a suffix"]
.
You can do anything in this before
callback. Maybe a custom .sort()
, .filter()
, .map()
to remap the entries, .slice()
to paginate only a subset of the data, etc!
Use JavaScript Template Functions here
Added in v2.0.0JavaScript Template Functions (which are also populated by universal filters and shortcodes) are available in the before
callback.
// β¦
before: function() {
let slug = this.slugify("My title.");
// use Universal filters or shortcodes tooβ¦
},
// β¦
Order of Operations
If you use more than one of these data set modification features, hereβs the order in which they operate:
- The
before
callback reverse: true
filter
entries
Add All Pagination Pages to Collections
By default, any tags listed in a paginated template will only add the very first page to the appropriate collection.
Consider the following pagination template:
---
tags:
- myCollection
pagination:
data: testdata
size: 2
testdata:
- item1
- item2
- item3
- item4
---
This means that collections.myCollection
will have only the first page added to the collection array (_site/my-page/index.html
). However, if youβd like to add all the pagination pages to the collections, use addAllPagesToCollections: true
to the pagination front matter options like so:
---
tags:
- myCollection
pagination:
data: testdata
size: 2
addAllPagesToCollections: true
testdata:
- item1
- item2
- item3
- item4
---
Now collections.myCollection
will have both output pages in the collection array (_site/my-page/index.html
and _site/my-page/1/index.html
).
Full Pagination Option List
data
(String) Lodash.get path to point to the target data set.size
(Number, required)alias
(String) Lodash.set path to point to the property to set.generatePageOnEmptyData
(Boolean) if target data set is empty, render first page with empty chunk[]
.resolve: values
filter
(Array)reverse: true
(Boolean)addAllPagesToCollections: true
(Boolean)
Related
From the Community
Γ48 resources courtesy of 11tybundle.dev curated by Bob Monsour
Expand to see 43 more resources.
Other pages in Eleventy Projects:
- Add CSS, JS, Fonts
- Layouts
- Collections
- Create Pages From Data
- Pagination
- Content Dates
- Permalinks
- Virtual Templates
- Internationalization (i18n)
- Programmatic API
- Quick Tips
- Common Pitfalls
- Deployment & Hosting