Hugo tips and tricks
Intro
Getting right into it, here’s a grab bag of techniques that I use in my Hugo site.
Disclaimer: one thing about Hugo is that there are many ways to do the same thing so I’ll update this post as I find more succinct or “correct” methods. If you know of a better way of doing any of these, please drop me a line . Post post scriptum, at time of writing I’m using Hugo version 0.96.0.
Compiling SASS
SASS
is one of many CSS pre-compiled languages. To get it working in Hugo means placing your SASS file, for example, in themes/<theme-name>/assess/sass/main.scss
and then adding this to the head section of your HTML template:
{{ $sass := resources.Get "sass/main.scss" }}
{{ $style := $sass | resources.ToCSS | minify | fingerprint }}
<link rel="stylesheet" href="{{ $style.Permalink }}" integrity="{{ $style.Data.Integrity }}">
Ordering by published date
I like to specify a published date for posts so that I can have a bunch of posts queued up and publish them over time instead of when they are completed. Hugo supports this out of the box with a publishDate
tag in your front matter but your templates need to take this into account to get the ordering to respect that.
Next or previous post
Having a next and previous link on a post (like I do at the bottom) can be accomplished with this convoluted snippet:
<ul>
{{ $pages := where $.Site.Pages.ByPublishDate.Reverse "Type" "posts" }}
{{ range $key, $page := $pages }}
{{ if eq $ . }}
{{ $prev := (index $pages (sub $key 1)) }}
{{ with $prev }}
<li><a title="{{ .Title }}" href="{{ .Permalink }}">« newer</a></li>
{{ end }}
{{ $next := (index $pages (add $key 1)) }}
{{ with $next }}
<li><a title="{{ .Title }}" href="{{ .Permalink }}">older »</a></li>
{{ end }}
{{ end }}
{{ end }}
</ul>
Posts listing
The listing of posts ordered by publish date is a bit simpler and can be done with this kind of template snippet:
<ul>
{{ range (.Paginate .Pages.ByPublishDate.Reverse).Pages }}
<li>
<article>
<h2><a title="{{ .Title }}" href="{{ .Permalink }}">{{ .Title }}</a></h2>
<div class="date">//{{ .PublishDate.Format "Jan 2, 2006" }}</div>
</article>
</li>
{{ end }}
</ul>
Setting the page description
The page description meta tag is relatively easy to set, but you might want to pull it from different places depending on the page. This is what I have in my baseof.html
file:
<meta name="description" content="{{- block "description" . -}}{{- if .IsHome -}}{{- .Summary -}}{{- else -}}{{- with .Description -}}{{- . -}}{{- else -}}{{- if .IsPage -}}{{- .Summary -}}{{- else -}}{{- with .Site.Params.description -}}{{- . -}}{{- end -}}{{- end -}}{{- end -}}{{- end -}}{{- end -}}">
But Michael, wtf does that monstrosity do? Something like:
- First define a “description” block that can be replaced, inside that:
- Check if it’s the home page, if so:
- Use the
.Summary
- Use the
- Otherwise:
- Try to use the
.Description
:- Output that here
- If no
.Description
:- Check if it’s a normal page:
- If so output the
.Summary
- If so output the
- Otherwise:
- Try to use the site description parameter
- Check if it’s a normal page:
- Try to use the
- Check if it’s the home page, if so:
External link icons
It can be polite to visitors to indicate that a link is external to the current site like this
. Furthermore, you probably want those links opening in another window or tab. Hugo let’s you automate that by using a render hook template, which is called every time a link is encountered by the Markdown parser. To get this working you’ll need a render-link.html
file in your theme’s layouts/_default/_markup
directory containing something like:
<a href="{{ .Destination | safeURL }}" title="{{ .Title }}"{{ if strings.HasPrefix .Destination "http" }} target="_blank"{{ end }}>
{{- .Text | safeHTML -}}
{{- if strings.HasPrefix .Destination "http" -}}
<svg ... />
{{- end -}}
</a>
Replace the svg
tag with an svg icon of your choice. Personally I went with a feather icon
.
Header links
To get links appearing on headers is similar to external links and is done through a render hook template. This time the template is render-heading.html
and is placed in the same directory as the external link template.
Here is the template I am using:
{{ $class := index .Attributes "class" }}
{{ $style := index .Attributes "style" }}
<h{{ .Level }} id="{{ .Anchor | safeURL }}"{{ if ne $class nil }} class="{{ $class }}" {{ end }}{{ if ne $style nil }} style="{{ $style }}" {{ end }}>
<a class="header-link nohover" href="#{{ .Anchor | safeURL }}">
<svg ... />
</a>{{- .Text | safeHTML -}}
</h{{ .Level }}>
Again, same deal regarding the svg
part. You may wonder what the $class
and $style
items are. To use those involves specifying a special attributes section after a heading such as:
## Heading
{style="color: red" .special}
… and without that in the above template those attributes won’t get placed in.
Table of contents
To render a table of contents at the top of the post you’ll need to do the following:
- Configure the
[markup.tableOfContents]
section in yourconfig.toml
(the defaults will do) - Add in front matter param such as
toc
which you then set totrue
orfalse
depending on whether it should show for the post - In your post template add a snippet such as:
{{ if .Params.toc }}
<section id="toc">
{{ .TableOfContents }}
</section>
{{ end }}
Post templates
When creating new posts or pages you most likely want consistent front matter tags already specified. To do that just drop in a markdown file with the name of the type in the archetypes
directory. For example, this is what I have in my archetypes/posts.md
file:
---
title: "{{ replace .Name "-" " " | title }}"
summary:
date: {{ .Date }}
publishDate:
tags: []
series:
toc: false
draft: true
---
After that, creating a post that uses this template is as simple as mashing in hugo new posts/your-new-post
into your terminal.
Last modified date
By default the last modified date will pull from the lastmod
front matter field, however, if you have put your site under version control using Git (and I recommend you do), then you can configure Hugo to pull the date from there if lastmod
isn’t set in your front matter. Just put this into your config.toml
file:
[frontmatter]
lastmod = ["lastmod", ":git", "date", "publishDate"]
Then .Lastmod
can be used in your templates, e.g.
<span>//Updated: {{ .Lastmod.Format "January 2, 2006" }}</span>
Note that you will have to commit to git first before this will show the right date.
HTML in markdown
If can be very handy to throw in the odd bit of HTML into your Markdown files but by default it will be stripped unless you set unsafe = true
in the [markup.goldmark.renderer]
section of your config.toml
file.
Series handling
Instead of having extremely long mega posts and then updating them, alternatively splitting posts into a series (such as this one on Hugo publishing) can make a lot of sense.
In terms of setup, all you have to do is set series = "series"
in your config.toml
under [taxonomies]
and then in your related posts, give the series
frontmatter tag the same value such as "Publishing with Hugo"
.
List posts in series
For your posts that are in a series, you will probably want to highlight that is the case by displaying a list of all related posts. That could something like this in a template:
{{ if .Params.series }}
{{ $series := index .Site.Taxonomies.series (.Params.series | urlize) }}
{{ if gt (len $series) 1 }}
<section id="series">
<summary>This is part of the series: {{ .Params.series }}</summary>
{{ $series := .Params.series }}
{{ $currentItem := .Permalink }}
<nav>
<ol>
{{ range $key, $page := $.Site.Pages.ByPublishDate }}
{{ if eq $page.Params.series $series }}
<li{{if eq $currentItem .Permalink}} class="active"{{ end }}><a href="{{ .Permalink }}">{{ .Title }}</a></li>
{{ end }}
{{ end }}
</ol>
</nav>
</section>
{{ end }}
{{ end }}
Syncing local to remote
Once you have created a new post you will want to update your website proper. How you do this will depend on your hosting, but for me it’s a matter of generating the files, commit the changes to git and rsyncing it across. Et voilà:
#!/bin/bash
Reset='\033[0m'
Green='\033[33;32m'
# We do this first so that hugo picks up the modified dates
echo -e "\n${Green}Pre publish...${Reset}"
git commit -message "Pre publish."
git push
echo -e "\n${Green}Generating...${Reset}"
hugo
git commit -message "Publish."
git push
echo -e "\n${Green}Syncing remote...${Reset}"
rsync -avz -e 'ssh -p <port> -i ~/.ssh/<key_file>' ./public <remote-user>@<remote-ip>:<remote-dir>
echo -e "\n${Green}Done${Reset}"
If you are using the published date concept, you might want to throw the above into a script that gets called by cron once a day.
And there you have it. Next time, I’ll cover ways to enhance the editing experience itself.
- May 30, 2022 ( Feb 8, 2024)