Paginate a listing in Stati

One line of front matter for numbered pages, and a pager built from stati.pagination.

SNP 147 #693 Filed stati eta

yaml
---
# site/blog/index.md
title: Blog
paginate: 10
---

Everything I've written, newest first.
eta
<% /* The listing part of site/blog/index.eta */ %>
<% const { items, pageNumber, totalPages, prevUrl, nextUrl, urls } = stati.pagination; %>

<% if (pageNumber === 1) { %>
  <%~ stati.content %>
<% } %>

<% for (const post of items) { %>
  <h2><a href="<%= post.url %>"><%= post.frontMatter.title %></a></h2>
<% } %>

<% if (totalPages > 1) { %>
  <nav aria-label="Pages">
    <% if (prevUrl) { %><a href="<%= prevUrl %>" rel="prev">Newer</a><% } %>
    <% urls.forEach((url, i) => { %>
      <% if (i + 1 === pageNumber) { %>
        <span aria-current="page"><%= i + 1 %></span>
      <% } else { %>
        <a href="<%= url %>"><%= i + 1 %></a>
      <% } %>
    <% }) %>
    <% if (nextUrl) { %><a href="<%= nextUrl %>" rel="next">Older</a><% } %>
  </nav>
<% } %>
  • Page 1 keeps /blog, and the rest are /blog/page/2, /blog/page/3 and so on, each its own HTML file. paginate: { size: 10, path: 'p' } changes the page in those.
  • Every page renders with the same layout and the same Markdown, hence the pageNumber === 1 around the introduction.
  • Read the posts from stati.pagination.items. A page then rebuilds only when its own slice or the number of pages changes, while one that reads stati.collection depends on the whole listing.
  • It works on any page that has a collection, so the home page can paginate the blog with collection: /blog, as in listing a section's posts anywhere.
  • The later pages are in the sitemap but not in the search index, the navigation or the feeds. To keep them out of the sitemap too, add excludePatterns: ['/blog/page/**'] to sitemap.

In the Stati docs: Pagination, the paginated listing recipe and Where generated and paginated pages appear.