How to customise Hugo sections

Do you want to supercharge your Hugo sections? There are several great tips here for you.

Ron Erdos
Updated March 9, 2020
Tested with Hugo version 0.74.3

Hugo sections overview

Let’s go over how Hugo sections work before customising them.

Hugo automatically generates sections if you have subfolders in your content folder

If you use subfolders to organise your posts, Hugo will automatically create sections for this content for you.

For example, let’s say you have content on Mars, as well as content on Venus.

And let’s say you organise your Hugo content like this:

📁 content
  📁 mars
     is-there-life-on-mars.md
     red-planet.md
  📁 venus
     second-brightest.md

Given the above, you’d end up with the following URIs:

/mars/
/mars/is-there-life-on-mars/
/mars/red-planet/

/venus/
/venus/second-brightest/

The first two URIs in each group above—/mars/ and /venus/—are our section indexes; the rest of the URIs are blog posts.

Sections appear in Hugo blog post urls by default

As you can see above, whatever you name your section folder will appear in blog post URLs from that section.

For example, the blog post /venus/second-brightest/ contains the /venus/ subfolder.

You can eliminate the section subfolder from Hugo blog post urls

In Hugo, you can overwrite the url to eliminate the subfolder.

However, in this tutorial we’ll be using the default URL setting we’ve been exploring, i.e. where section subfolder names appear in blog post URLs.

Hugo also generates section index pages by default

Hugo also automatically generates index pages for each section. These are the /mars/ and /venus/ pages we discussed above.

However, if your theme doesn’t have a customised layout file for sections, then it will likely look quite boring.

I’ll now show you how to make these section index pages more interesting.

How to customise section indexes to make them more interesting

What if we wanted a custom h1 heading of “Destination Mars”? And what if we wanted some custom intro text before the list of Mars blog posts?

The way to do that is below. There are two parts.

Part 1: Store custom headings, descriptions, and images in an _index.md file

We’ll need to keep our custom h1 heading (“Destination Mars”) and our custom intro text in a special file named _index.md, which we’ll need to store in the root (top level) of our Mars section.

In other words, the file will need to live at /content/mars/_index.md.

Code for playing along

First, create a (blank) new file under /content/mars/ and name it _index.md. Don’t forget the leading underscore in the filename.

Secondly, copy and paste in the following text into your _index.md file and save it:

title: "Destination Mars"
summary: "When will we land on the Red Planet?"

Part 2: Create a custom layout named section.html

OK, so we’ve stored our custom text in the right place, now we need to create a layout file to call that custom text.

Code for playing along

Create a blank new file named section.html in the /layouts/_default/ subfolder that’s included automatically in each Hugo site.

Inside this /layouts/_default/section.html file, copy and paste the below:

<h1>{{ .Title }}</h1>
<p>{{ .Summary }}</p>
<div>
	{{ range .Pages }}
		{{ .Render "li" }}
	{{ end }}
</div>

Note that you’ll also need to include any header or footer partials that you use elsewhere on your site. You can learn more about partials by scrolling down about a screen’s worth.

HTML output

So now our rendered /mars/ section homepage will contain this HTML:

<h1>Destination Mars</h1>
<p>When will we land on the Red Planet?</p>
<!-- Blog posts below -->

Pretty cool right?

Example of a customised Hugo section homepage

You can see a live example of a customised section homepage right here on this site.

How to get different email signup forms on posts from different sections

What if you want to allow people to sign up for email updates just for Mars posts, or just for Venus posts?

You’d need a custom email signup form for each section. I do that on this site, by the way.

I’ll show you how to do that, but first, you need to know about a Hugo feature called “partials”.

OK, so let’s say we have a subscribe partial in our single blog post template. In this subscribe partial we’ll put our email signup forms.

Code for playing along

Our single blog post template, /layouts/_default/single.html, might look like this:

{{ partial "header.html" . }}

<h1>{{ .Title }}</h1>
{{ .Content }}

{{ partial "subscribe.html" . }}
{{ partial "footer.html" . }}

So there are three partials in there: the header, the footer, and the email signup forms.

All pretty straightforward.

However, what if you want a different signup form for each section? One for Venus, one for Mars?

You do it in the subscribe partial itself, using Hugo’s if and else if statements:

Code for playing along

In our subscribe partial, /layouts/partials/subscribe.html, we see:

{{ if eq .Section "mars" }}
	<!-- Mars signup form goes here -->
{{ else if eq .Section "venus" }}
	<!-- Venus signup form goes here-->
{{ end }}

Nested partials (Hugo partials inside partials)

You can also use nested partials (partials inside other partials) in Hugo.

So rather than putting the two sets of email signup forms (one for Mars, one for Venus) directly in the subscribe partial like we did above … (draws breath) … you could first put each form in its own partial, then use them in the conditional logic:

{{ if eq .Section "mars" }}
	{{ partial "subscribe-mars.html" . }}
{{ else if eq .Section "venus" }}
	{{ partial "subscribe-venus.html" . }}
{{ end }}

That way, you’d be free to use just the Mars email signup form elsewhere on the site, by calling {{ partial "subscribe-mars.html" . }}

How to get different RSS feeds in different Hugo sections

In a similar way to the section-specific email signup form work we did above, what if you wanted a different RSS feed for each section?

Hugo does most of the work for us, but not all. Read on to find out how to do this.

How does RSS work in Hugo by default?

Even if you don’t alter any RSS-related code, Hugo will create a bunch of different RSS feeds for you, right out of the box.

To start, there’ll be one for each section; that is, one for each folder in the root of your Hugo /content folder.

So let’s say we had the same file structure as we saw earlier in this tutorial:

📁 content
  📁 mars
     is-there-life-on-mars.md
     red-planet.md
  📁 venus
     second-brightest.md

You can see that we have two sections, one for Mars, the other for Venus.

Hugo will automatically generate RSS feeds at:

/mars/index.xml

/venus/index.xml

It will also generate a top-level RSS feed at:

/index.xml

which will contain content on both Mars and Venus.

How to eliminate the top-level RSS feed and just use the section-specific RSS feeds

I’m not sure if there’s an easy way to suppress publication of the top-level RSS feed (i.e. /index.xml), but I can show you how to encourage feed readers to only present your section-specific RSS feeds to readers.

OK, here’s how to do that:

Code for playing along

In the <head> section of your homepage, include this code:

{{ range .Site.Sections }}
	<link rel="alternate" type="application/rss+xml"
	title="{{ .Site.Title }} {{ title .Section }} Articles"
	href="{{ .Site.BaseURL }}{{ .Section }}/index.xml">
{{ end }}

As usual, I’ve broken up the code over a few lines so you can read this tutorial more easily on your mobile device. However, your production code should keep the link element all on one line.

Now let’s go through the code, line by line.

{{ range .Site.Sections }} starts a loop over each site section. In our case, that’s mars and venus.

<link rel="alternate" type="application/rss+xml" starts our RSS link. This is all standard, nothing to customise here.

title="{{ .Site.Title }} {{ title .Section }} Articles" generates the title of the RSS link. We’re using two variables here.

The first variable is {{ .Site.Title }}. This is the name of your site, and it’s pulled from your config.toml file, specifically the title field.

The second variable is {{ title .Section }}. The .Section part pulls the name of section subfolders (e.g. mars or venus), while title turns them into Title Case (Mars and Venus).

Note that I’ve also hardcoded Articles to the end of the link title, so it will read, for example, “MoonBooth Mars Articles”.

href="{{ .Site.BaseURL }}{{ .Section }}/index.xml"> generates the actual link itself. There are two variables in here, one that we saw a second ago.

The first variable is {{ .Site.BaseURL }}, and this also looks in your config.toml file, but this time, it’s looking for the baseURL field. For example: baseURL = "https://example.com/".

And the second variable we saw a second ago, it’s {{ .Section }}, and it will fetch mars and venus as the names of the subfolders under your Hugo /content/ folder.

I’ve hardcoded /index.xml to the end of the link, so we’ll end up with, for example, https://example.com/mars/index.xml as the value of href.

{{ end }} closes the loop we opened a few lines above.

OK, let’s see how it looks!

HTML output

Putting that together, you end up with this output in your rendered homepage’s HTML:

<link rel="alternate"
type="application/rss+xml"
title="MoonBooth Mars Articles"
href="https://example.com/mars/index.xml">

<link rel="alternate"
type="application/rss+xml"
title="MoonBooth Venus Articles"
href="https://example.com/venus/index.xml">

Note that Hugo pulls the sections in alphabetical order, which is why we see Mars before Venus.

"Thanks so much for your work ... I'm migrating my WordPress blog to Hugo and it's been really helpful." — Francisco S., engineer and blogger

"I love your content. The information that you provide have been very useful in the development of my personal website." — Mattia C., engineer and researcher

The planets in our solar system