Hugo RSS Feed With One Section

Published June 10, 2021
Photo by Taras Shypka on Unsplash

For this blog I wanted a RSS feed so people can easily subscribe to updates.

I’m using Hugo to generate this site from Markdown files into HTML, so naturally Hugo takes care of generating sitemaps and RSS feeds too.

A quote from Hugo’s docs states this:

Hugo ships with its own RSS 2.0 template. The embedded template will be sufficient for most use cases.

And they’re right! But they’re not what I expected them to be. What I expected was an RSS feed with my posts that gets updated whenever I post new content. Instead, it contains all my pages, content and posts.

I went on a search to other people facing this problem and while I found some other blog posts about it, there was not a single solution that worked immediately. Instead of making it a quick copy and paste fix it sent me on a journey to fix this issue.

Without further ado, let’s see some code.

For my layouts/_default/rss.xml I use this template:

{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>{{ .Site.Title }}</title>
        <link>{{ .Permalink }}</link>
        <description>Recent content on {{ .Site.Title }}</description>
        <generator>Hugo -- gohugo.io</generator>{{ with .Site.LanguageCode }}
        <language>{{.}}</language>{{end}}{{ with .Site.Author.email }}
        <managingEditor>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</managingEditor>{{end}}{{ with .Site.Author.email }}
        <webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }}
        <copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }}
        <lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
        {{ with .OutputFormats.Get "RSS" }}
            {{ printf "<atom:link href=%q rel=\"self\" type=%q />" .Permalink .MediaType | safeHTML }}
        {{ end }}

        <!-- Because my site has content in multiple languages, but blog posts only in English, it merges all posts -->
        {{ $pages := (where .Site.RegularPages "Section" "posts") }}
        {{ range .Translations }}
            {{ $pages = $pages | lang.Merge (where .Site.RegularPages "Section" "posts") }}
        {{ end }}

        {{ range $pages }}
            <item>
                <title>{{ .Title }}</title>
                <link>{{ .Permalink }}</link>
                <pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
                {{ with .Site.Author.email }}<author>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</author>{{end}}
                <guid>{{ .Permalink }}</guid>
                <description>{{ "<![CDATA[" | safeHTML }} {{ .Summary }}]]></description>
            </item>
        {{ end }}
    </channel>
</rss>

The important part in this template is the where .Site.RegularPages "Section" "posts" part. What this does is that it actually filters all regular pages (children) that belong to the specified Section.

One last thing you need to do to make RSS feeds actually a nice experience is include a link to them in the <head> of your pages:

{{ range .AlternativeOutputFormats -}}
<link
    rel="{{ .Rel }}"
    type="{{ .MediaType.Type }}"
    href="{{ .Permalink }}"
    title="{{ $.Site.Title }}"
/>
{{ end -}}