Pagination in Hugo Hextra theme

Pagination in Hugo Hextra theme

June 20, 2025

I recently switched this blog to use the Hugo Hextra theme. Compared with the previous theme I was using, Hextra had a search function that actually worked correctly, and it had a more pleasing and simpler appearance. But one problem I noticed right away was that it didn’t support pagination in the listing of posts: the summaries of all 283 posts for this blog appeared on one page.

After examining the official Hugo documentation for pagination, I was able to apply a simple patch to the theme’s layout, and create some custom CSS for the pagination controls.

Configuration

In order to enable pagination and specify the number of posts to appear on one page in the blog listing, create a configuration parameter in hugo.yaml called blog.list.pageSize. If present, this variable specifies the number of posts that should appear on a page in the post listing. If it is not present, the post listing will not be paginated.

Here is how I specified a page size of 10 posts in hugo.yaml:

params:
  blog:
    list:
      displayTags: true
      pageSize: 10

Layout

At the project top level directory, copy the theme’s layouts/blog/list.html to the project’s own layout:

mkdir layouts/blog
cp themes/hextra/layouts/blog/list.html layouts/blog/list.html

Then apply the following patch to layouts/blog/list.html:

--- themes/hextra/layouts/blog/list.html	2025-06-21 16:30:19.640005204 -0700
+++ layouts/blog/list.html	2025-06-22 05:08:24.840819850 -0700
@@ -8,6 +8,10 @@
         {{ if .Title }}<h1 class="hx:text-center hx:mt-2 hx:text-4xl hx:font-bold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100">{{ .Title }}</h1>{{ end }}
         <div class="content">{{ .Content }}</div>
         {{- $pages := partial "utils/sort-pages" (dict "page" . "by" site.Params.blog.list.sortBy "order" site.Params.blog.list.sortOrder) -}}
+        {{- if site.Params.blog.list.pageSize -}}
+          {{- $paginator := .Paginate $pages site.Params.blog.list.pageSize -}}
+          {{- $pages = $paginator.Pages -}}
+        {{- end -}}
         {{- range $pages }}
           <div class="hx:mb-10">
             <h3><a style="color: inherit; text-decoration: none;" class="hx:block hx:font-semibold hx:mt-8 hx:text-2xl " href="{{ .RelPermalink }}">{{ .Title }}</a></h3>
@@ -23,6 +27,9 @@
             <p class="hx:opacity-50 hx:text-sm hx:mt-4 hx:leading-7">{{ partial "utils/format-date" .Date }}</p>
           </div>
         {{ end -}}
+        {{- if site.Params.blog.list.pageSize -}}
+          {{ partial "pagination.html" . }}
+        {{- end -}}
       </main>
     </article>
     <div class="hx:max-xl:hidden hx:h-0 hx:w-64 hx:shrink-0"></div>

Given the configuration above, this produces pages of 10 posts each on the post list page. It also produced the desired pagination controls at the bottom of the page, but its appearance is unsatisfactory, with each control on a separate line.

Custom CSS

To fix the appearance of the controls, I added some custom CSS that reproduced the appearance of the pagination controls in the previous theme I’d been using.

At the project top level directory, create the directory assets/css, then copy the following to assets/css/custom.css:

assets/css/custom.css
.pagination {
  display: flex;
  border-radius:.25rem;
  margin:2rem 1rem;
  padding:.5rem 0;
  align-items:center;
  justify-content:center;
  /*background-color:#eee;*/
  border-style: solid;
  border-width: 1px;
}

.page-item {
  padding: 5px;
  display: inline;
}

.pagination li {
  border-radius:.25rem
}

.pagination li.active {
  color: #26a69a;
}

.pagination li.disabled a:hover,
.pagination li.disabled a:active,
.pagination li.disabled a:focus {
  /*color:#757575;*/
  text-decoration:none
}

.pagination a {
  font-size:1.25rem;
  padding:.5rem .75rem
}

Here is what the pagination controls should look like now:

pagination controls