Where to Discuss?

Local Group

Preface

Goal: Add indicator, and putting all pagination part together.


1: Source

I respect copyright. Most of the code below, especially middle pagination, copied and pasted from:

The rest is my modification.


2: Prepare

Config

Pagination can be set in config.toml

Paginate     = 7

Layout: List

The complete file is here below:

{{ define "main" }}
<main role="main" 
      class="container-fluid m-3 m-sm-0 p-3
             bg-light rounded border border-dark shadow-hover">
  <header>
    <h4>{{ .Title | default .Site.Title }}</h4>
  </header>

  <article>
    {{ .Content }}
  </article>

  {{ $paginator := .Paginate (where .Site.Pages "Type" "post") }}
  {{ partial "pagination-indicator.html" (dict "p" $paginator "s" .Scratch) }}
  <section id="archive">
  <div class="post-list">
    {{ range $paginator.Pages }}
      {{ partial "summary-blog-list.html" . }}
    {{ end }}
  </div>
  </section>
</main>
{{ end }}

Partial: Minimal Pagination Code

You should have this minimal code, before you begin.

<nav aria-label="Page navigation">
  {{ $s := .s }}
  {{ $p := .p }}

  {{ if gt $p.TotalPages 1 }}
  <ul class="pagination justify-content-center">

  ...

  </ul>
  {{ end }}

</nav>

Dictionary

In order to pass variable from list layout to pagination partial, we utilize dictionary: (dict “p” $paginator).

  • themes/tutor-05/layouts/archives/list.html.
  {{ partial "pagination-indicator.html" (dict "p" $paginator "s" .Scratch) }}

And later call it here:

  • themes/tutor-05/layouts/partials/pagination-indicator.html.
  {{ $s := .s }}
  {{ $p := .p }}

3: Preview: General

Structure

This consist of at least seven parts:

  • Previous Page: «

  • First Page: always 1

  • Left Indicator

  • Middle Pagination: Glenn McComb

  • Right Indicator

  • Last Page: always the same number

  • Next Page: »

We will not discuss about Middle Pagination, as it has already been discussed in previous article.

HTML Preview

The HTML that we want to achieve is similar as below.

  <ul class="pagination justify-content-center">
      <li class="page-item blog_previous">...</li>
      <li class="page-item first">...</li>
      <li class="pages-indicator first disabled">...</li>

      <li class="page-item">...</li>
      <li class="page-item">...</li>
      <li class="page-item active ">...</li>
      <li class="page-item">...</li>
      <li class="page-item">...</li>

      <li class="pages-indicator last disabled">...</li>
      <li class="page-item last">...</li>
      <li class="page-item blog_next">...</li>
  </ul>

Small Preview

This is the complete version.

Hugo Pagination: Combined Animation

Wide Preview

Wide version, in responsive context, is slightly different.

Hugo Pagination: Preview Wide

We will use responsive CSS later to achieve this effect.

Partial: Pagination Code Skeleton

As usual, the skeleton, to show the complexity.

<nav aria-label="Page navigation">
  {{ $s := .s }}
  {{ $p := .p }}

  {{ if gt $p.TotalPages 1 }}
  <ul class="pagination justify-content-center">

    <!-- Variable Initialization. -->

    <!-- Previous Page. -->
    <!-- First Page. -->
    <!-- Early (More Pages) Indicator. -->

    {{- range $p.Pagers -}}
      {{ $s.Set "page_number_flag" false }}

      <!-- Complex page numbers. -->
      {{ if gt $p.TotalPages $max_links }}
        <!-- Lower limit pages. -->
        <!-- Upper limit pages. -->
        <!-- Middle pages. -->

      <!-- Simple page numbers. -->
      {{ else }}
        ...
      {{ end }}

      <!-- Show Pager. -->
      {{- if eq ($s.Get "page_number_flag") true -}}
      ...
      {{- end -}}
    {{ end }}

    <!-- Late (More Pages) Indicator. -->
    <!-- Last Page. -->
    <!-- Next Page. -->

  </ul>
  {{ end }}

</nav>

Each Pagination

Consider again, have a look at the animation above, frame by frame.

We have from first page (1), to last page (10).

Hugo Pagination: Adjacent Page 1

Hugo Pagination: Adjacent Page 2

Hugo Pagination: Adjacent Page 3

Hugo Pagination: Adjacent Page 4

Hugo Pagination: Adjacent Page 5

Hugo Pagination: Adjacent Page 6

Hugo Pagination: Adjacent Page 7

Hugo Pagination: Adjacent Page 8

Hugo Pagination: Adjacent Page 9

Hugo Pagination: Adjacent Page 10


4: Variables: Initialization

Consider refresh our memory, about our variables initialization.

<nav aria-label="Page navigation">
  {{ $s := .s }}
  {{ $p := .p }}

  {{ if gt $p.TotalPages 1 }}
  <ul class="pagination justify-content-center">

    <!-- Page numbers. -->
    {{- $pagenumber := $p.PageNumber -}}

    <!-- Number of links either side of the current page. -->
    {{ $adjacent_links := 2 }}

    <!-- $max_links = ($adjacent_links * 2) + 1 -->
    {{ $max_links := (add (mul $adjacent_links 2) 1) }}

    <!-- $lower_limit = 1 + $adjacent_links -->
    {{ $lower_limit := (add 1 $adjacent_links) }}

    <!-- $upper_limit = $paginator.TotalPages - $adjacent_links -->
    {{ $upper_limit := (sub $p.TotalPages $adjacent_links) }}

  </ul>
  {{ end }}
</nav>

5: Navigation: Previous and Next

It is similar to our simple pagination. Except that, now we use dictionary.

    <!-- Previous Page. -->
    {{ if $p.HasPrev }}
      <li class="page-item blog_previous">
        <a class="page-link" href="{{ $p.Prev.URL }}" rel="prev">&laquo;</a>
      </li>
    {{ else }}
      <li class="page-item blog_previous disabled">
        <span class="page-link">&laquo;</span>
      </li>
    {{ end }}
    <!-- Next Page. -->
    {{ if $p.HasNext }}
      <li class="page-item blog_next">
        <a class="page-link" href="{{ $p.Next.URL }}" rel="next">&raquo;</a>
      </li>
    {{ else }}
      <li class="page-item blog_next disabled">
        <span class="page-link">&raquo;</span>
      </li>
    {{ end }}

6: Navigation: First and Last

It is different to our simple pagination. Although it is based on the same logic.

This will not be shown, if it is already be shown middle pagination.

    {{ if gt $p.TotalPages $max_links }}
      <!-- First Page. -->
      {{ if gt (sub $p.PageNumber $adjacent_links) 1 }}
        <li class="page-item first">
          <a class="page-link" href="{{ $p.First.URL }}">1</a>
        </li>
      {{ end }}
    {{ end }}

This will not be shown, if it is already be shown middle pagination.

    {{ if gt $p.TotalPages $max_links }}
      <!-- Last Page. -->
      {{ if lt (add $p.PageNumber $adjacent_links) $p.TotalPages }}
        <li class="page-item last">
          <a class="page-link" href="{{ $p.Last.URL }}">{{ $p.TotalPages }}</a>
        </li>
      {{ end }}
    {{ end }}

7: Indicator: Left and Right

This will only be shown, if necessary.

Indicator: Left

    {{ if gt $p.TotalPages $max_links }}
      <!-- Early (More Pages) Indicator. -->
      {{ if gt (sub $p.PageNumber $adjacent_links) 2 }}
        <li class="pages-indicator first disabled">
          <span class="page-link">...</span>
        </li>
      {{ end }}
    {{ end }}

Indicator: Right

    {{ if gt $p.TotalPages $max_links }}
      <!-- Late (More Pages) Indicator. -->
      {{ if lt (add $p.PageNumber $adjacent_links) (sub $p.TotalPages 1) }}
        <li class="pages-indicator last disabled">
          <span class="page-link">...</span>
        </li>
      {{ end }}
    {{ end }}

8: Combined Code

It is about the right time to put all the code together.

Hugo Pagination: Combined Animation

<nav aria-label="Page navigation">
  {{ $s := .s }}
  {{ $p := .p }}

  {{ if gt $p.TotalPages 1 }}
  <ul class="pagination justify-content-center">

    <!-- Page numbers. -->
    {{- $pagenumber := $p.PageNumber -}}

    <!-- Number of links either side of the current page. -->
    {{ $adjacent_links := 2 }}

    <!-- $max_links = ($adjacent_links * 2) + 1 -->
    {{ $max_links := (add (mul $adjacent_links 2) 1) }}

    <!-- $lower_limit = 1 + $adjacent_links -->
    {{ $lower_limit := (add 1 $adjacent_links) }}

    <!-- $upper_limit = $paginator.TotalPages - $adjacent_links -->
    {{ $upper_limit := (sub $p.TotalPages $adjacent_links) }}

    <!-- Previous Page. -->
    {{ if $p.HasPrev }}
      <li class="page-item blog_previous">
        <a class="page-link" href="{{ $p.Prev.URL }}" rel="prev">&laquo;</a>
      </li>
    {{ else }}
      <li class="page-item blog_previous disabled">
        <span class="page-link">&laquo;</span>
      </li>
    {{ end }}

    {{ if gt $p.TotalPages $max_links }}
      <!-- First Page. -->
      {{ if gt (sub $p.PageNumber $adjacent_links) 1 }}
        <li class="page-item first">
          <a class="page-link" href="{{ $p.First.URL }}">1</a>
        </li>
      {{ end }}

      <!-- Early (More Pages) Indicator. -->
      {{ if gt (sub $p.PageNumber $adjacent_links) 2 }}
        <li class="pages-indicator first disabled">
          <span class="page-link">...</span>
        </li>
      {{ end }}
    {{ end }}

    {{- range $p.Pagers -}}
      {{ $s.Set "page_number_flag" false }}

      <!-- Complex page numbers. -->
      {{ if gt $p.TotalPages $max_links }}

        <!-- Lower limit pages. -->
        <!-- If the user is on a page which is in the lower limit.  -->
        {{ if le $p.PageNumber $lower_limit }}

          <!-- If the current loop page is less than max_links. -->
          {{ if le .PageNumber $max_links }}
            {{ $s.Set "page_number_flag" true }}
          {{ end }}

        <!-- Upper limit pages. -->
        <!-- If the user is on a page which is in the upper limit. -->
        {{ else if ge $p.PageNumber $upper_limit }}

          <!-- If the current loop page is greater than total pages minus $max_links -->
          {{ if gt .PageNumber (sub .TotalPages $max_links) }}
            {{ $s.Set "page_number_flag" true }}
          {{ end }}

        <!-- Middle pages. -->
        {{ else }}
          
          {{ if and ( ge .PageNumber (sub $p.PageNumber $adjacent_links) ) ( le .PageNumber (add $p.PageNumber $adjacent_links) ) }}
            {{ $s.Set "page_number_flag" true }}
          {{ end }}

        {{ end }}

      <!-- Simple page numbers. -->
      {{ else }}

        {{ $s.Set "page_number_flag" true }}
      {{ end }}

      {{- if eq ($s.Get "page_number_flag") true -}}
      <li class="page-item{{ if eq $pagenumber .PageNumber }} active{{ end }}">
        {{ if not (eq $pagenumber .PageNumber) }} 
          <a href="{{ .URL }}" class="page-link">{{ .PageNumber }}</a>
        {{ else }}
          <span class="page-link page-item">{{ .PageNumber }}</span>
        {{ end }}
      </li>
      {{- end -}}
    {{ end }}

    {{ if gt $p.TotalPages $max_links }}
      <!-- Late (More Pages) Indicator. -->
      {{ if lt (add $p.PageNumber $adjacent_links) (sub $p.TotalPages 1) }}
        <li class="pages-indicator last disabled">
          <span class="page-link">...</span>
        </li>
      {{ end }}

      <!-- Last Page. -->
      {{ if lt (add $p.PageNumber $adjacent_links) $p.TotalPages }}
        <li class="page-item last">
          <a class="page-link" href="{{ $p.Last.URL }}">{{ $p.TotalPages }}</a>
        </li>
      {{ end }}
    {{ end }}

    <!-- Next Page. -->
    {{ if $p.HasNext }}
      <li class="page-item blog_next">
        <a class="page-link" href="{{ $p.Next.URL }}" rel="next">&raquo;</a>
      </li>
    {{ else }}
      <li class="page-item blog_next disabled">
        <span class="page-link">&raquo;</span>
      </li>
    {{ end }}

  </ul>
  {{ end }}

</nav>

9: Responsive: Simple

Before we continue to the next pagination article about responsive, consider having this very simple example using bootstrap.

Hugo Pagination: Preview Wide

This will show the word previous and next for medium page.

SASS: Custom Pagination

I’m using Bootstrap 4 grid breakpoints.

@include media-breakpoint-up(md) {

  .blog_previous {
    span.page-link:after,
    a.page-link:after {
      content: " previous"
    }
  }

  .blog_next {
    span.page-link:before,
    a.page-link:before {
      content: "next "
    }
  }

}

SASS: Main

@import
  // taken from bootstrap
    "sticky-footer-navbar",
    "blog",
    "bootstrap-custom",

  // variables
    "bootstrap/functions",
    "variables",
    "bootstrap/variables",
    "bootstrap/mixins/breakpoints",

  // custom
    "layout",
    "decoration",
    "list",
    "pagination"
;

SASS: Bootstrap Grid Breakpoint Variables.

Bootstrap 4 grid breakpoints are defined as below.

  • themes/tutor-05/sass/bootstrap/_variables.scss
// Grid breakpoints
//
// Define the minimum dimensions at which your layout will change,
// adapting to different screen sizes, for use in media queries.

$grid-breakpoints: (
  xs: 0,
  sm: 576px,
  md: 768px,
  lg: 992px,
  xl: 1200px
) !default;

What is Next ?

There are, some interesting topic about Pagination in Hugo. Consider continue reading [ Hugo - Pagination - Responsive ].

Thank you for reading.