Pagination in Zola
  • Tawhid Hannan
    Retweet
    Created
    Last Updated
  • Tags:

    I've been experimenting with Zola, a static site generator. The documentation for pagination is pretty good from an anatomical perspective, but I thought there was room for something a little more 'recipe' like. So, this is how I personally implemented pagination on my Zola site, aiming to demonstrate the nuts and bolts in action. This demonstration will be correct for zola binaries compatible with 0.12.0.

    The Goal

    Imagine a homepage, much like the one for this site. Let's assume there's 15 posts, sorted by most recent posts first, but we don't want to overwhelm the user or have them scroll forever, so we'd like to display a maximum of five posts at once. We need to be able to click on the posts themselves to get to the articles, but also we want to make it possible to see beyond the most recent five posts.

    Configuring the index page

    These index pages are configured by markdown frontmatter. So at the root of your content directory, you'll want a file, _index.md, who's sole purpose is to configure how to render a particular section. Now by creating this file at the root of the entire content directory, the 'section' is essentially all of the content in the site - perfect for a homepage.

    The frontmatter of this markdown page, is a set of attributes that must come at the start of every markdown file that Zola renders. These attributes are specified between two rows of three + characters, which Zola uses as the bookends for the frontmatter.

    +++
    (ATTRIBUTES GO HERE)
    +++
    

    One attribute available to use is the paginate_by attribute, enabling us to specify how many posts per page should be rendered. In accordance with the goals set, we can make that 5.

    +++
    paginate_by = 5
    +++
    

    To ensure the pages are sorted by date, we can also specify how to sort these pages. Let's do that.

    +++
    sort_by = date
    paginate_by = 5
    +++
    

    According to the documentation, the semantics of specifying the sort_by attribute to date are as follows:

    This will sort all pages by their date field, from the most recent (at the top of the list) to the oldest (at the bottom of the list). Each page will get page.earlier and page.later variables that contain the pages with earlier and later dates, respectively.

    At this point, all the configuration is in place to leverage pagination. However, before pagination actually works, we need to render this list of posts in a template. Now, we can specify the template in the frontmatter, but by default, for the root _index.md file, this will resolve to the root index.html file.

    Rendering

    As a foundation, we'll build up the following template.

    <!DOCTYPE html>
    <html lang="en">
      <body>
        <main>
        </main>
      </body>
    </html>
    

    Note that this is an incredibly simplified template, with the goal of illustrating pagination. Your template probably will look different.

    Zola makes certain variables available to each html template file. The variables we're concerned with are variables within the paginator namespace. Now, the paginator namespace isn't guaranteed to be available to the template - if the _index.md file the template is rendering doesn't have any pagination settings enabled, the namespace won't exist.

    Safety measures

    As a precautionary measure, we can check if it exists before trying to do anything with it. In Zola, we can run any templating logic between curly braces with percentage symbols inside, like so:

    <!DOCTYPE html>
    <html lang="en">
      <body>
        <main>
          {% if paginator %}
          {% endif %}
        </main>
      </body>
    </html>
    

    Listing Pages

    We can now write any and all logic related to pagination between these two added lines, and safely interact with the paginator namespace. There exists a variable on the paginator, pages, which corresponds to an array of pages. We can list these pages out 'iterator' style - let's just list out some basic links with the corresponding dates. It should look like the code below:

    <!DOCTYPE html>
    <html lang="en">
      <body>
        <main>
          {% if paginator %}
          <div>
            {% for post in paginator.pages %}
            <div>
              <span>{{post.date}}</span>
              <a href="{{ post.permalink }}">{{ post.title }}</a>
            </div>
            {% endfor %}
          </div>
          {% endif %}
        </main>
      </body>
    </html>
    

    At this point, the homepage renders up to 5 posts at once, with the most recent posts first. Great start, but we need to enable users to get to the older pages should they so desire - this is the actual pagination part!

    Pagination

    The paginator namespace exposes pretty much all the variables we need for this. It would be good to display the current page, and some links to the next and previous pages if there are any. If there aren't, we can either leave off the buttons entirely or go with disabling them - I'm going to render disabled buttons. To achieve all this, we're going to need three variables from the paginator namespace - previous, next and current_index.

    Let's get these done in order.

    Previous Page

    The paginator.previous variable corresponds to a link to the previous page, if such a page exists - so we'll check if it exists, and if it exists, render a link to the page with the text 'previous', and if it doesn't exist, display a disabled version of the same link. For the disabled version, instead of linking to the page,

    <!DOCTYPE html>
    <html lang="en">
      <body>
        <main>
          {% if paginator %}
          <div>
            {% for post in paginator.pages %}
            <div>
              <span>{{post.date}}</span>
              <a href="{{ post.permalink }}">{{ post.title }}</a>
            </div>
            {% endfor %}
          </div>
          {% endif %}
          {% if paginator.previous %}
            <a href="{{ paginator.previous }}">&lt;</a>
          {% else %}
            &lt;
          {% endif %}
        </main>
      </body>
    </html>
    

    Current Page

    This is pretty easy - we pretty much just render paginator.current_index into the page.

    <!DOCTYPE html>
    <html lang="en">
      <body>
        <main>
          {% if paginator %}
          <div>
            {% for post in paginator.pages %}
            <div>
              <span>{{post.date}}</span>
              <a href="{{ post.permalink }}">{{ post.title }}</a>
            </div>
            {% endfor %}
          </div>
          {% endif %}
          {% if paginator.previous %}
            <a href="{{ paginator.previous }}">&lt;</a>
          {% else %}
            &lt;
          {% endif %}
          <span>{{ paginator.current_index }}</span>
        </main>
      </body>
    </html>
    

    Next Page

    Here, we can pretty much do what we did with the 'previous' page example.

    <!DOCTYPE html>
    <html lang="en">
      <body>
        <main>
          {% if paginator %}
          <div>
            {% for post in paginator.pages %}
            <div>
              <span>{{post.date}}</span>
              <a href="{{ post.permalink }}">{{ post.title }}</a>
            </div>
            {% endfor %}
          </div>
          {% endif %}
          {% if paginator.previous %}
            <a href="{{ paginator.previous }}">&lt;</a>
          {% else %}
            &lt;
          {% endif %}
          <span>{{ paginator.current_index }}</span>
          {% if paginator.next %}
            <a href="{{ paginator.next }}">&gt;</a>
          {% else %}
            &gt;
          {% endif %}
        </main>
      </body>
    </html>
    

    Calling It

    This is a minimal implementation for a homepage that renders up to five pages, and allows clicking through to view other pages, using Zola. This should be sufficient as a starting point for expanding on, customising and styling. I'm looking to move this blog to something Zola flavored, and I'll talk about why in the future - suffice to say, I'm pleased with how easy pagination is to roll out.