Pagination
The p:paginate attribute splits an element's direct children across numbered pages during server-side composition. Children outside the current page are removed from the response, and navigation links are generated automatically.
When to reach for it
Use pagination when a composed element produces more children than should appear in a single response. Pagination runs after includes, expression bindings, and templates have evaluated — it operates on the fully-resolved DOM.
Attribute form
<ul id="items" xmlns:p="https://pagelove.org/1.0" p:paginate="10">
<li>Item 1</li>
<li>Item 2</li>
<!-- ... -->
</ul>
The attribute value is a positive integer specifying the default page length. When multiple paginators exist in the same document, each element must have an id attribute.
Query parameters
Clients navigate pages using query parameters. Pages are 1-indexed.
| Parameter | Purpose | Default |
|---|---|---|
paginate:page |
Page number | 1 |
paginate:length |
Items per page | Attribute value |
For a single paginator:
/contacts.html?paginate:page=2
/contacts.html?paginate:page=2&paginate:length=20
When multiple paginators exist, prefix with the element's id:
/dashboard.html?paginate:users:page=2&paginate:posts:page=3
Invalid values (non-numeric, zero, negative) fall back to defaults silently. A requested page beyond the total is clamped to the last page.
Navigation links
Pagination generates <link> elements in <head> and HTTP Link headers (RFC 8288) for navigation:
GET /paginate-docs/contacts.html?paginate:page=2&paginate:length=3
HTTP/1.1 200
<!DOCTYPE html>
<html>
<head><title>Contacts</title><link rel="first" href="?paginate:page=1&paginate:length=3" title="contacts"><link rel="prev" href="?paginate:page=1&paginate:length=3" title="contacts"><link rel="next" href="?paginate:page=3&paginate:length=3" title="contacts"><link rel="last" href="?paginate:page=3&paginate:length=3" title="contacts"></head>
<body>
<ul id="contacts">
<li>Dave</li>
<li>Eve</li>
<li>Frank</li>
</ul>
</body>
</html>
Generated links include rel="first", rel="prev", rel="next", and rel="last" as appropriate. All non-pagination query parameters are preserved in the generated URLs.
Examples
Single page
When all children fit on one page, no navigation links are generated:
GET /paginate-docs/short.html
HTTP/1.1 200
<!DOCTYPE html>
<html>
<head><title>Short List</title></head>
<body>
<ul id="items">
<li>Alpha</li>
<li>Beta</li>
<li>Gamma</li>
</ul>
</body>
</html>
With Range selectors
Pagination works with the Range header. When a request includes Range: selector=ul#contacts, the full document is composed first (pagination, templates, bindings), then the selector extracts the paginated fragment:
GET /paginate-docs/contacts.html?paginate:page=2&paginate:length=3
Range: selector=ul#contacts
HTTP/1.1 206
Content-Range: selector=ul#contacts
<ul id="contacts">
<li>Dave</li>
<li>Eve</li>
<li>Frank</li>
</ul>
This supports AJAX-style page navigation — fetch the paginated list fragment without the surrounding document.
Multiple paginators
A document can contain multiple independently paginated elements. Each must have a unique id:
<section>
<ul id="users" p:paginate="5"><!-- user items --></ul>
<ul id="posts" p:paginate="10"><!-- post items --></ul>
</section>
Navigate them independently:
?paginate:users:page=2&paginate:posts:page=3
Each paginator's generated links preserve the other paginators' current state.
Error cases
| Condition | Result |
|---|---|
p:paginate value is not a positive integer |
422 error response |
Multiple paginators without id attributes |
422 error response |
| Page number exceeds total pages | Clamped to last page |
| No children in paginated element | Element returned as-is, no links generated |
See also
- Templating — templates evaluate before pagination
- Expression Binding — bindings evaluate before pagination
- Includes — includes resolve before pagination