Adding links to previous and next articles in Hugo
I've just added a browse navigation to the single page templates of this blog. Below each article (or page, as they are called in the Hugo universe), you will now find links to the previous and next article. This typical blog navigation allows users to browse from article to article without the need to return to a list page.
Since the documentation in Hugo is not super straight forward, i want to document my insights here.
Finding the suitable page variables
First off, i didn't really know where to start looking within the Hugo documentation. After 3 years of working on the template, my Hugo skills got a bit rusty. The Template section only has pagination for lists. Searching for "previous next" leads to the example template for a single page. Here, the page variables .PrevInSection
and .NextInSection
are used. This didn't sound suitable and indeed is not what i need since i have organized my articles in yearly folders, hence each year is a section. However, the use of page variables was the necessary hint. Here we also find .Prev
and .Next
.
The entry for .Prev
states:
Points down to the previous regular page (sorted by Hugo’s default sort). Example:
{{if .Prev}}{{.Prev.Permalink}}{{end}}
. Calling.Prev
from the last page returnsnil
.
The entry for .Next
states:
Points up to the next regular page (sorted by Hugo’s default sort). Example:
{{with .Next}}{{.Permalink}}{{end}}
. Calling.Next
from the first page returnsnil
.
Interestingly, the example code differs. One uses the conditional if
, the other uses with
.
Which one to use?
The Conditionals section in the introduction to Hugo) provides descriptions for both conditionals. Turns out, with
is a very convenient way in Hugo to use the logic of an if
condition while simultaniously binding the context of the used variable to the code block.
What does "binding the context" mean? In my not-so-computer-sciency own words: Hugo grabs all related variables and shoves it down into that with
-block so we don't need to use that variable name anymore within that code block. Let's look again at the code examples from above and see how the output for the permalink is written. The {{if .Prev}}
statement requires {{.Prev.Permalink}}
, whereas the {{with .Next}}
statement requires {{.Permalink}}
to achieve the same result.
We DRY up our code by using the with
conditional. This is also encouraged by the Hugo documentiontation:
It is common to write “if something exists, do this” kind of statements using with.
Final template
In my final HTML template, the Page variable .Permalink
is used for the link attribute href
. The displayed link label is the title of the article, using the Page-level Parameter .Params.Title
.
Here's the relevant code, reduced to the necessary parts.
<nav aria-label="Browse">
{{with .Next}}
<a href="{{.Permalink}}">{{.Params.Title}}</a>
{{end}}
{{with .Prev}}
<a href="{{.Permalink}}">{{.Params.Title}}</a>
{{end}}
</nav>
Complete code snippet, with accessibility considerations
And this is the complete code (both HTML and CSS), including the arrows as well as the link back to the home page.
Please note, that i have added aria-hidden="true"
to all arrows and added separate aria-labels for the links. While the HTML code is translated into and read aloud as a semantic word by a screen reader, the announcement "heavy arrow headed right" is hardly a useful information. Instead, i prefix the link label with "Next article:" and "Previous article:" respectively. This has the effect that the links can be easier recognized when listing all links of a page within a screen reader.
(Now one could argue that a simple arrow is in itself also not a useful information for sighted users. However, this being a known pattern on blogs and having a potential association with a timeline, albeit reversed, i'd say the arrows provide enough information for sighted users. And yes, this is opening up a whole new field for discussion ;).)
<nav aria-label="Browse" class="post-single__browse browse-nav">
{{with .Next}}
<div class="browse-nav__next">
<div aria-hidden="true" class="browse-nav__symbol browse-nav__symbol--next">
➞
</div>
<a href="{{.Permalink}}" class="browse-nav__link browse-nav__link--next" aria-label="Next article: {{.Params.Title}}">{{.Params.Title}}</a>
</div>
{{end}}
{{with .Prev}}
<div class="browse-nav__prev">
<div aria-hidden="true" class="browse-nav__symbol browse-nav__symbol--prev">
➞
</div>
<a href="{{.Permalink}}" class="browse-nav__link browse-nav__link--prev" aria-label="Previous article: {{.Params.Title}}">{{.Params.Title}}</a>
</div>
{{end}}
<a href="{{.Site.BaseURL}}" class="browse-nav__home"><span aria-hidden="true">⇤</span> Back to home page</a>
</nav>
.browse-nav {
display: grid;
gap: 3rem 5rem;
grid-template-areas: "next prev" "home home";
grid-template-columns: repeat(2, 1fr);
}
.browse-nav__prev {
grid-area: prev;
justify-self: end;
}
.browse-nav__next {
grid-area: next;
}
.browse-nav__home {
grid-area: home;
}
.browse-nav__symbol {
text-align: right;
}
.browse-nav__symbol--next {
transform: rotate(180deg);
}
.browse-nav__link--prev {
display: inline-block;
text-align: right;
}