Listings & pagination
A paginate block generates an index page over a collection. Its presence is what turns the index on.
content {
collections {
notes {
sort "order"
paginate { template "list.typ" }
}
}
}
That’s one page at /notes/ holding every member, in the collection’s sort order. A collection’s own template wraps each member; the index over them is a different page and needs its own.
Add a size when the collection is long enough to split:
content {
collections {
blog {
sort "date"
reverse #true
paginate { size 5; template "list.typ" }
}
}
}
Now the build writes /blog/, /blog/page/2/, /blog/page/3/ and so on, five entries each, with previous and next links. Splitting is a modifier on a listing, not a separate feature: the same template renders both.
Keys
| Key | Type | Default | Does |
|---|---|---|---|
size |
int | – | Members per index page. Without one, the index is a single page. |
template |
str | – | The layout the index renders through. |
mount |
str | /{collection}/ |
Where page 1 is served. |
prefix |
str | page |
The path segment before a page number. |
mount "/" puts a blog on the site root, while /blog/page/2/ and on keep the normal layout. An empty prefix numbers pages directly under the collection:
content {
collections {
blog { paginate { size 5; prefix "p" } }
news { paginate { size 5; prefix "" } }
}
}
| Config | Page 1 | Page 2 |
|---|---|---|
| default | /blog/ |
/blog/page/2/ |
prefix "p" |
/blog/ |
/blog/p/2/ |
prefix "" |
/blog/ |
/blog/2/ |
mount "/" |
/ |
/blog/page/2/ |
An index page is titled after its collection, capitalized. Page 2 onwards appends the localized word for page and the number: Blog - page 2.
What the template receives
The index is an ordinary templated page, so it looks like the rest of your site. Its data arrives as page.frontmatter, structured, never as HTML:
#let list(page, body) = {
page.frontmatter.title
for entry in page.frontmatter.entries {
link(entry.url)[#entry.label]
}
let nav = page.frontmatter.nav
if nav.next != none { link(nav.next)[Older] }
}
Each entry is the same shape everywhere a page appears as data, so one card component renders a collection index, a term page and a home-page grid:
| Field | Type | Holds |
|---|---|---|
url |
str | The page’s permalink. |
label |
str | Its title. |
collection |
str | The collection it belongs to. Empty for a row with no page behind it. |
lang |
str | Its language code. |
date |
str or none | Its date as ISO-8601, for a <time datetime>. |
display |
str or none | The same date written the way the page’s language writes one. |
note |
str or none | A trailing annotation, such as a term’s member count. |
description |
str or none | Its one-line summary, from description or the summary alias. |
image |
str or none | Its own social image. |
alt |
str or none | What that image shows. Empty marks it decorative. |
author |
str or none | Who wrote it. The page’s own; never the site default. |
taxonomies |
dict | Its terms, keyed by taxonomy. |
extra |
dict | The frontmatter baudelaire does not name: the theme’s own keys. |
WARN
page.frontmatter.nav on a listing carries plain URL strings for the previous and next index page. page.nav, on every page, carries (url, title) dicts for the neighboring content pages. Two different things with similar names.
An index generated for an empty collection still gets its page 1, since nav links point at it and an empty listing beats a 404.