Paginating long posts
In many online publications, long posts are split across multiple pages with pagination at the bottom. How can we achieve this in Kirby?
In this example, we will insert manual page breaks where we want to split the post. We take the notes section from the starterkit as our example setup.
The page break
In a note
post, add an HTML comment where you want to paginate:
Title: Across the ocean
----
Text:
Far far away, behind the word mountains, far from the countries Vokalia and Consonantia, there live the blind texts.
<!--nextpage-->
Separated they live in Bookmarksgrove right at the coast of the Semantics, a large language ocean. A small river named Duden
The controller
Add a new note.php
controller in /site/controllers/
<?php
return function ($page) {
// split the text into parts where we have inserted the pagebreak
$parts = explode('<!--nextpage-->', $page->text());
// Apply Kirbytext to every part
$parts = array_map('kirbytext', $parts);
// create a new collection from the parts and paginate
$parts = (new Collection($parts))->paginate(1);
// create the pagination object
$pagination = $parts->pagination();
// return the variables to the template
return compact('parts', 'pagination');
};
The template
In the note.php
template, we have to make three changes.
First, we loop through the parts we defined in the controller:
<?php foreach ($parts as $part): ?>
<!-- rest of code -->
<?php endforeach ?>
Secondly, we replace the original line where we displayed the complete text…
<?= $page->text()->kirbytext() ?>
with just the part of the text:
<?= $part ?>
And finally, we add the pagination with links for the previous and next pagination pages at the bottom:
<nav class="pagination">
<?php if ($pagination->hasPrevPage()): ?>
<a href="<?php echo $pagination->prevPageUrl() ?>">‹ previous page</a>
<?php endif ?>
<?php if ($pagination->hasNextPage()): ?>
<a href="<?php echo $pagination->nextPageUrl() ?>">next page ›</a>
<?php endif ?>
</nav>
Here is the complete template:
<?php snippet('header') ?>
<main>
<?php foreach ($parts as $part): ?>
<article class="note">
<header class="note-header intro">
<h1><?= $page->title() ?></h1>
<time class="note-date"><?= $page->date()->toDate('d F Y') ?></time>
<?php if ($page->tags()->isNotEmpty()) : ?>
<p class="note-tags tags"><?= $page->tags() ?></p>
<?php endif ?>
</header>
<div class="note-text text">
<?= $part ?>
</div>
</article>
<?php endforeach ?>
</main>
<nav class="pagination">
<?php if ($pagination->hasPrevPage()): ?>
<a href="<?php echo $pagination->prevPageUrl() ?>">previous page</a>
<?php endif ?>
<?php if ($pagination->hasNextPage()): ?>
<a href="<?php echo $pagination->nextPageUrl() ?>">next page</a>
<?php endif ?>
</nav>
<?php snippet('footer') ?>
Going beyond
Instead of using manual page breaks, you could split your text by other criteria, e.g after every x paragraphs, number of words or characters, before subheading etc.
Or you could add a little table of contents on each page.
You can also extend the pagination navigation and add a numbered link for each page, making use of the $pagination->range()
method. You can find an example in our pagination recipe.