Pagination
Pagination splits a list of content across multiple pages. SuCoS provides first-class pagination through the paginate Liquid filter and the page.Paginator object.
How pagination works
Pagination in SuCoS is template-driven: the theme template decides whether to paginate and how many items to show per page. Content authors don't need to configure anything.
When a template calls | paginate: N, SuCoS:
- Slices the provided list into pages of N items each
- Creates virtual pages (e.g.
/blog/page/2/,/blog/page/3/) for pages beyond the first - Exposes a
Pagerobject the template uses to render navigation
Basic example
{% comment %} themes/my-theme/_default/list.html {% endcomment %}
{% assign sorted = page.Pages | sort: 'Date' | reverse %}
{% assign pager = sorted | paginate: 10 %}
{% for post in pager.PageItems %}
<article>
<h2><a href="{{ post.Permalink }}">{{ post.Title }}</a></h2>
<time>{{ post.Date | date: '%B %d, %Y' }}</time>
</article>
{% endfor %}
{% render 'partials/pagination.html', pager: pager %}
pager.PageItems contains only the items for the current page — the filtering happens automatically based on the current URL.
Automatic pagination
If you omit the number of items, SuCoS uses the Paginate value from your sucos.yaml:
{% assign pager = sorted | paginate %}
The Pager object
...
Pagination Performance
SuCoS is optimized for large paginated collections. Since v6.5.0, several caching layers ensure that even sites with thousands of pages remain fast:
- Template Caching: Compiled Fluid templates are cached, so the pagination logic doesn't need to be re-parsed for every sub-page.
- Content Caching: Rendered HTML for each page is cached, preventing Markdig from re-processing the same content across different paginated views.
- Memory Efficiency: SuCoS uses optimized data structures like
FrozenDictionaryfor internal lookups, keeping memory usage low during large builds.
The paginate filter returns a Pager with these properties:
| Property | Type | Description |
|---|---|---|
PageItems |
page list | Items for the current page |
Current |
int | Current page number (1-based) |
Count |
int | Total number of pages |
First |
string | URL of the first page |
Last |
string | URL of the last page |
Prev |
string | URL of the previous page (nil on page 1) |
Next |
string | URL of the next page (nil on last page) |
Pages |
int list | All page numbers (useful for numbered nav) |
BaseUrl |
string | Base URL without the page segment |
PaginatePath |
string | URL segment (default: page) |
Configuring defaults
Set defaults in sucos.yaml:
Paginate: 10 # items per page
PaginatePath: page # URL segment: /blog/page/2/
These are the defaults; you can override items-per-page per template by passing a different number to the filter.
Also available in templates:
{{ site.Paginate }}
{{ site.PaginatePath }}
Built-in pagination partial
SuCoS ships a built-in partials/pagination.html that renders previous/next navigation. Use it with:
{% render 'partials/pagination.html', pager: pager %}
Output (simplified):
<nav>
<a href="/blog/">← Previous</a>
<a href="/blog/page/2/">Next →</a>
</nav>
Custom pagination navigation
Override the built-in partial by creating themes/my-theme/partials/pagination.html:
{% if pager.Count > 1 %}
<nav class="pagination" aria-label="Page navigation">
{% if pager.Prev %}
<a href="{{ pager.Prev }}" class="btn">← Previous</a>
{% endif %}
<span>Page {{ pager.Current }} of {{ pager.Count }}</span>
{% if pager.Next %}
<a href="{{ pager.Next }}" class="btn">Next →</a>
{% endif %}
</nav>
{% endif %}
Numbered pagination
Use pager.Pages to render a numbered nav bar:
<nav class="pagination">
{% for num in pager.Pages %}
{% if num == pager.Current %}
<span class="current">{{ num }}</span>
{% else %}
{% if num == 1 %}
<a href="{{ pager.BaseUrl }}">{{ num }}</a>
{% else %}
<a href="{{ pager.BaseUrl }}{{ site.PaginatePath }}/{{ num }}/">{{ num }}</a>
{% endif %}
{% endif %}
{% endfor %}
</nav>
URL structure
With the default PaginatePath: page and a blog at /blog/:
| Page | URL |
|---|---|
| First page | /blog/ |
| Second page | /blog/page/2/ |
| Third page | /blog/page/3/ |
The first page always uses the section's base URL, not /blog/page/1/.
Pagination across sections
Paginate any collection, not just the current page's children:
{% assign allPosts = site.RegularPages | where: 'Section', 'blog' | sort: 'Date' | reverse %}
{% assign pager = allPosts | paginate: 5 %}
This is useful for home pages that paginate across the entire blog.