« Back to home

Archive Pages

I recently converted this blog over from a WordPress installation to use static pages generated by Hugo. I’ve used Hugo for my other recent web creations and I’ve been very impressed. The templating system is very nice, the page generation is lightning fast, and I’m always able to find something near what I need in the themes.

One WordPress feature I liked was the listing of articles by year and month… My blog had a little timeline on the side with each year and month listed, annotated with the number of postings I made during each. This let me look back through my ramblings chronologically, which is how a lot of them are organized in my head.

Tags, categories, and year/month were each ways I could peruse my scrawlings, and I’ve got occasion to turn to each.

Hugo didn’t do that. Categories and tags will, by default, generate index pages you can use to browse associated entries. Not dates. A few people have requested this feature or created workarounds already.

Wordpress had a widget where you’d click on the month associated with the year you were interested in, then it’d take you to a page listing all relevant posts.

I don’t need that indirection… I’ve got under 150 posts - even with 1000 it wouldn’t be crazy to list all the titles in a row.

Here’s my solution to this - this is the content in layouts/archive/list.html:

{{ partial "header" . }}
    <a class="btn home" href="{{ .Site.BaseURL }}" title="{{ .Site.Data.l10n.article.back_to_home }}">&laquo; {{ .Site.Data.l10n.article.back_to_home }}</a>

    {{ $.Scratch.Set "open" 0 }}
    {{ $.Scratch.Set "month" 13 }}
    {{ $.Scratch.Set "year" 0 }}
    {{ range $.Site.Pages.ByDate.Reverse }}
        {{ if and .IsPage (in .Site.Params.mainSections .Section) }}
            {{ $last_yr := $.Scratch.Get "year" }}
            {{ $last_mn := $.Scratch.Get "month" }}
            {{ if ne .Date.Year $last_yr }}
                {{ if eq ($.Scratch.Get "open") 1 }}
                    </ul>
                    {{ $.Scratch.Set "open" 0 }}
                {{ end }}
                <h1>{{ .Date.Year }}</h1>
                {{ $.Scratch.Set "year" .Date.Year }}
                {{ $.Scratch.Set "month" 13 }}
            {{ end }}
            {{ if ne .Date.Month $last_mn }}
                {{ if eq ($.Scratch.Get "open") 1 }}
                    </ul>
                    {{ $.Scratch.Set "open" 0 }}
                {{ end }}
                <h2>{{ .Date.Month }}</h2>
                {{ $.Scratch.Set "month" .Date.Month }}
                <ul>
                {{ $.Scratch.Set "open" 1 }}
            {{ end }}
            <li><a href="{{ .Permalink }}">{{ .Title }}</a></li>
        {{ end }}
    {{ end }}
    {{ if eq ($.Scratch.Get "open") 1 }}
        </ul>
    {{ end }}

{{ partial "footer" . }}

I’m using the hugo-minimalist-theme by digitalcraftsman, but most themes have a header and footer partial, and here it just does exactly what you’d expect.

This uses a couple scratch variables to keep track of the output state… It sorts the posts chronologically first, then goes backwards over them. When it notes a change in month or year, it closes the old unordered post list, outputs a header for the new year/month, then creates a new unordered list before outputting the post titles as list elements. It must then keep track of whether a list is actually open or not.

The result is the archive page you see linked on the left… It’s just a long list of year, sub-lists of months, then sub-lists of posts in chronological order. This provides the same look-back ability in a way that is visually appealing, and also lets me Ctrl+F a post title… Post title search was something else difficult with Hugo that is now solved.

NOTE: You’ll also need to create a file in content/archive/_index.md with at least the minimum front matter (title, I recommend…)