Implementing blog theme bells and whistles in Hugo: pagination, pages, related posts, and tag lists
January 21, 2016
I recently redesigned this blog, and added a few things that you’d generally expect in a blog design but aren’t intuitive to implement in Hugo (yet). Specifically, I added tag lists, related posts, pagination, and some “about” pages. Here’s how to do these things.
And all the code for my blog is on GitHub if it’s helpful.
Pagination
There’s pretty good docs page for this, but I couldn’t figure out from it what I actually needed to do. Here are the steps to get pagination working:
1) Make sure the range
statement in your index template includes Paginator. For example,
{{ $baseurl := .Site.BaseURL }}
{{ partial "header.html" . }}
<div id="index">
{{ range (.Paginator 10).Pages }}
<article class="post">
<h1><a href="{{ .RelPermalink }}">{{ .Title }}</a></h1>
<p class="post-time">{{ .Date.Format "January 2, 2006" }}</p>
<div class="post-content">
<p>{{ .Summary }}</p>
<a class="btn btn-default" href="{{.RelPermalink}}" id="read-more-button">
Read more >
</a>
</div>
</article>
{{ end }}
</div>
So in this code block, the header is at the beginning of the file as you’d expect, then we open up the div that will contain all the summaries, and then the range
is inserted with Paginator.
2) Add the Paginator block code at the bottom. You should be able to use this as is:
{{ if .Paginator.HasPrev }}
<a class="btn btn-default" href="{{ .Paginator.Prev.URL }}">
< Previous Page
</a>
{{ end }}
{{ if .Paginator.HasNext }}
<a class="btn btn-default" href="{{ .Paginator.Next.URL }}">
Next Page >
</a>
{{ end }}
Related posts
The first step with related posts in Hugo is to keep your expectations low. (Fortunately, that’s the first step with almost all related posts functionality for blogs.)
Then take a look at dim0627’s solution for this problem. This was the best solution I found. Basically, it:
1) Grabs the tags and permalink for the current post
{{ $page_link := .Permalink }}
{{ $tags := .Params.tags }}
2) Goes through every page on your site and checks to see if there’s an intersection between the current post’s tags ($tags)
and that posts’s tags
{{ range .Site.Pages }}
{{ $page := . }}
{{ $has_common_tags := intersect $tags .Params.tags | len | lt 0 }}
3) If it does, compares this page’s permalink with the current page’s permalink (to make sure the related post isn’t the very post you’re dealing with currently)
{{ if and $has_common_tags (ne $page_link $page.Permalink) }}
<li><a href="{{ $page.Permalink }}">{{ $page.Title }}</a></li>
{{ end }}
{{ end }}
One problem with this is that I’m not smart enough to get this to stop after returning just one related post. I used CSS to hide all but the first:
#related-posts > a:nth-of-type(n+3), #related-posts > h3:nth-of-type(n+1) {
display: none;
}
“About” pages
To explain this one a bit better: I wanted a set of normal HTML pages that I could post without a date or other metadata. They needed to exist outside of Hugo’s normal system for adding and listing other content, and also to allow me to add arbitrary HTML without anything being added to them.
This is a bit tricky to do, and I cobbled together my solution from a few sources. Here’s how it works:
1) Create normal Hugo markdown files for the pages you want. For example, I have one called about.md. This file is of a new type, which I called _static.
+++
type = "_static"
page = "_static/about.html"
+++
2) We’ll use that page
parameter to tell Hugo what HTML file to render, through a few steps. First, add a layout file for the _static type (_static/single.html). single.html looks like this:
{{ if .Params.page }}
{{ partial .Params.page . }}
{{ else }}
{{ end }}
So basically, all it does is tell Hugo to go look for a correspondingly-named partial.
3) The partial is the .html file you want to render. For example, under partials/_static/about.html, I have:
{{ $baseurl := .Site.BaseURL }}
{{ partial "header.html" . }}
<article class="post">
<div class="post-content">
<h1>About</h1>
<p>I'm a digital marketing nerd and marketing engineer. I specialize in using software and data to execute effective, focused marketing strategies that drive revenue.</p>
And so on.
Tag lists
Every post on my blog now shows tags: x, y, and z where the tags are linked correctly. I used halostatue’s solution for this. It works well, and I couldn’t find another solution for having a list of tags that was linked correctly.