Collections and Pagination

Content Collections and Pagination

Pages can define content collections via a content block in frontmatter. The collection specifies which pages to include, how to filter and sort them, and how many to show per page. The result is exposed as page.collection in the template context, with pagination metadata in page.pagination.

Collection Frontmatter

---
title: Blog
template: blog
content:
  items: "@self.children"        # Source: children of current page
  order:
    by: date                     # Sort field: date, title, order, url
    dir: desc                    # Direction: asc or desc
  limit: 10                     # Items per page (enables pagination)
  filter:
    tags: [rust]                 # Only pages with these tags
    author: "John Doe"           # Only pages by this author
    published: true              # Only published pages (default)
    custom:                      # Only pages with these frontmatter values
      category: praxis
---

filter.custom matches your own frontmatter keys. A page is kept when it matches every key you list: its value equals the one you give, or, when the page’s value is a list, any element equals it. This is how you group pages by an author-defined key – a category, a product type, an event track – without turning it into a tag.

The match is by value and type: a YAML string matches a string, a number a number, a boolean a boolean. featured: true in the filter matches a page’s featured: true but not featured: "true", and year: 2024 does not match year: "2024". Write the filter value the same way you write it in the page’s frontmatter.

The items field currently supports @self.children to collect child pages of the current page. On a site with several languages, the children are the pages of the current page’s language, as page.children and page.siblings are.

Sorting

order.by accepts date, title, order, or url, with dir set to asc or desc.

Sorting by date is chronological, not alphabetical: each page’s date is parsed into a calendar date before comparison, so every format Accent accepts sorts in true date order – ISO (2026-06-14, and non-zero-padded forms like 2026-5-24), US (06/14/2026), European (14.06.2026), and long or short month (June 14, 2026, Jun 14, 2026). You do not need to zero-pad or normalise dates for them to sort correctly. Pages whose date is missing or unrecognised sort first in ascending order (last in descending); ties keep a stable, deterministic order.

Collection Template

Each item in page.collection has the same properties as pages in children or siblings (title, url, date, author, lead, tags, order). It also carries custom, a mapping of the page’s own frontmatter keys, so a listing can show or test them: {% for p in all_pages if p.custom.category == "praxis" %} or {{ post.custom.subtitle }}. The same custom mapping is on all_pages, page.children, page.siblings, taxonomy.pages, and recent_documents().

{% for post in page.collection %}
<article>
    <h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
    {% if post.lead %}<p>{{ post.lead }}</p>{% endif %}
    {% if post.date %}<time>{{ post.date }}</time>{% endif %}
    {% if post.tags %}
    <div class="tags">
        {% for tag in post.tags %}<a href="{{ tag_url(tag) }}">{{ tag }}</a>{% endfor %}
    </div>
    {% endif %}
</article>
{% endfor %}

Pagination

When content.limit is set, Accent CMS automatically paginates the collection. Pagination metadata is available in page.pagination:

PropertyTypeDescription
page.pagination.current_pageintCurrent page number (1-based)
page.pagination.total_pagesintTotal number of pages
page.pagination.total_itemsintTotal number of items in the collection
page.pagination.items_per_pageintItems shown per page
page.pagination.prev_urlstring/noneURL to the previous page (none on page 1)
page.pagination.next_urlstring/noneURL to the next page (none on last page)

Pagination URLs append /page/{num} to the page URL, for example /blog/page/2. Page 1 is the page URL itself. The same URL works under accent serve and on a static host, because it is the path the build writes the page to. An older link in the /blog/page:2 form redirects permanently to /blog/page/2 under accent serve.

Because /page/{num} is pagination, give a paginated page no child page named page with numbered children: accent serve shows the child page, while the build writes both to the same file.

{% if page.pagination %}
<nav class="pagination" aria-label="Pagination">
    {% if page.pagination.prev_url %}
    <a href="{{ page.pagination.prev_url }}">Previous</a>
    {% endif %}
    <span>Page {{ page.pagination.current_page }} of {{ page.pagination.total_pages }}</span>
    {% if page.pagination.next_url %}
    <a href="{{ page.pagination.next_url }}">Next</a>
    {% endif %}
</nav>
{% endif %}

Static Build

The accent build command generates HTML files for all pagination pages. Page 1 is written to output/{path}/index.html, and subsequent pages to output/{path}/page/{num}/index.html, the path the pagination links name, so they work on any static host.

Content Relationships

Pages can define named relationships to other pages via the relations frontmatter field. Each relationship is a named list of page references that are resolved into full PageMetaContext objects at render time.

Relations Frontmatter

---
title: Advanced Patterns
relations:
  series: ["part-1", "part-2", "part-4"]
  related: ["/docs/advanced", "/blog/deep-dive"]
  prerequisites: ["/docs/getting-started"]
---

References can be:

  • Absolute URLs: Starting with /, resolved directly (e.g., /docs/getting-started)
  • Relative slugs: Resolved from the current page’s parent URL (e.g., on /blog/part-3, the slug part-2 resolves to /blog/part-2)

Broken references (pointing to non-existent pages) are logged as warnings and omitted from the result. Relationship names are arbitrary strings defined by the content author.

Relations in Templates

Resolved relations are available as page.relations.<name>, where each entry is a page metadata object with title, url, date, author, lead, tags, and order.

Series navigation:

{% if page.relations.series %}
<nav class="series-nav">
    <h3>In this series</h3>
    <ol>
    {% for part in page.relations.series %}
        <li>
            {% if part.url == page.url %}
                <strong>{{ part.title }}</strong> (current)
            {% else %}
                <a href="{{ part.url }}">{{ part.title }}</a>
            {% endif %}
        </li>
    {% endfor %}
    </ol>
</nav>
{% endif %}

Related articles sidebar:

{% if page.relations.related %}
<aside class="related">
    <h3>Related Articles</h3>
    <ul>
    {% for article in page.relations.related %}
        <li><a href="{{ article.url }}">{{ article.title }}</a></li>
    {% endfor %}
    </ul>
</aside>
{% endif %}

Prerequisites notice:

{% if page.relations.prerequisites %}
<div class="prerequisites">
    <p>Before reading this, you should be familiar with:</p>
    <ul>
    {% for prereq in page.relations.prerequisites %}
        <li><a href="{{ prereq.url }}">{{ prereq.title }}</a></li>
    {% endfor %}
    </ul>
</div>
{% endif %}

The default theme template automatically renders series, related, and prerequisites relations when present.