Skip to content

Kirby 3.9.8

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:

/content/notes/note.txt
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/

/site/controllers/note.php
<?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:

/site/templates/note.php
<?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.