I’ve found RSS useful over the years and think that the world would be a marginally better place if more websites supported it (luckily, many still do, but newer websites sometimes do not). With that in mind, I decided to make sure that this website supports it.

Hugo RSS Configuration

Adding RSS to a hugo site (such as this, if you’re interested in getting started please see here) is relatively easy, just add the sections documented at here to your hugo.json/toml/yaml file.

Once added, you might notice that you are not completely satisfied with your hugo produced RSS feed. By default, Hugo generates a summary based on the first 70 words of your post (documented here). Without care, this might generate article descriptions you do not like. Hugo supports overriding the automated summary and documents here.

Overriding the Default RSS Layout

You also might also want to provide the full-text over RSS 1. If so, you will need to override the default rss layout.

To do this, the first step is to add the layouts/_default/ folder to your project and create a rss.xml file there. The contents of this file, initially, can be pulled from here. The initial file might look something like this:

{{- /* Deprecate site.Author.email in favor of site.Params.author.email */}}
{{- $authorEmail := "" }}
{{- with site.Params.author }}
  {{- if reflect.IsMap . }}
    {{- with .email }}
      {{- $authorEmail = . }}
    {{- end }}
  {{- end }}
{{- else }}
  {{- with site.Author.email }}
    {{- $authorEmail = . }}
    {{- warnf "The author key in site configuration is deprecated. Use params.author.email instead." }}
  {{- end }}
{{- end }}

{{- /* Deprecate site.Author.name in favor of site.Params.author.name */}}
{{- $authorName := "" }}
{{- with site.Params.author }}
  {{- if reflect.IsMap . }}
    {{- with .name }}
      {{- $authorName = . }}
    {{- end }}
  {{- else }}
    {{- $authorName  = . }}
  {{- end }}
{{- else }}
  {{- with site.Author.name }}
    {{- $authorName = . }}
    {{- warnf "The author key in site configuration is deprecated. Use params.author.name instead." }}
  {{- end }}
{{- end }}

{{- $pctx := . }}
{{- if .IsHome }}{{ $pctx = .Site }}{{ end }}
{{- $pages := slice }}
{{- if or $.IsHome $.IsSection }}
{{- $pages = $pctx.RegularPages }}
{{- else }}
{{- $pages = $pctx.Pages }}
{{- end }}
{{- $limit := .Site.Config.Services.RSS.Limit }}
{{- if ge $limit 1 }}
{{- $pages = $pages | first $limit }}
{{- end }}
{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>{{ if eq .Title .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{ . }} on {{ end }}{{ .Site.Title }}{{ end }}</title>
    <link>{{ .Permalink }}</link>
    <description>Recent content {{ if ne .Title .Site.Title }}{{ with .Title }}in {{ . }} {{ end }}{{ end }}on {{ .Site.Title }}</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>{{ site.Language.LanguageCode }}</language>{{ with $authorEmail }}
    <managingEditor>{{.}}{{ with $authorName }} ({{ . }}){{ end }}</managingEditor>{{ end }}{{ with $authorEmail }}
    <webMaster>{{ . }}{{ with $authorName }} ({{ . }}){{ 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 }}
    {{- range $pages }}
    <item>
      <title>{{ .Title }}</title>
      <link>{{ .Permalink }}</link>
      <pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
      {{- with $authorEmail }}<author>{{ . }}{{ with $authorName }} ({{ . }}){{ end }}</author>{{ end }}
      <guid>{{ .Permalink }}</guid>
      <description>{{ .Summary | transform.XMLEscape | safeHTML }}</description>
    </item>
    {{- end }}
  </channel>
</rss>

What you want to change is the tag located at the end. Instead of .Summary, use .Content. Your file should now look like this.

{{- /* Deprecate site.Author.email in favor of site.Params.author.email */}}
{{- $authorEmail := "" }}
{{- with site.Params.author }}
  {{- if reflect.IsMap . }}
    {{- with .email }}
      {{- $authorEmail = . }}
    {{- end }}
  {{- end }}
{{- else }}
  {{- with site.Author.email }}
    {{- $authorEmail = . }}
    {{- warnf "The author key in site configuration is deprecated. Use params.author.email instead." }}
  {{- end }}
{{- end }}

{{- /* Deprecate site.Author.name in favor of site.Params.author.name */}}
{{- $authorName := "" }}
{{- with site.Params.author }}
  {{- if reflect.IsMap . }}
    {{- with .name }}
      {{- $authorName = . }}
    {{- end }}
  {{- else }}
    {{- $authorName  = . }}
  {{- end }}
{{- else }}
  {{- with site.Author.name }}
    {{- $authorName = . }}
    {{- warnf "The author key in site configuration is deprecated. Use params.author.name instead." }}
  {{- end }}
{{- end }}

{{- $pctx := . }}
{{- if .IsHome }}{{ $pctx = .Site }}{{ end }}
{{- $pages := slice }}
{{- if or $.IsHome $.IsSection }}
{{- $pages = $pctx.RegularPages }}
{{- else }}
{{- $pages = $pctx.Pages }}
{{- end }}
{{- $limit := .Site.Config.Services.RSS.Limit }}
{{- if ge $limit 1 }}
{{- $pages = $pages | first $limit }}
{{- end }}
{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>{{ if eq .Title .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{ . }} on {{ end }}{{ .Site.Title }}{{ end }}</title>
    <link>{{ .Permalink }}</link>
    <description>Recent content {{ if ne .Title .Site.Title }}{{ with .Title }}in {{ . }} {{ end }}{{ end }}on {{ .Site.Title }}</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>{{ site.Language.LanguageCode }}</language>{{ with $authorEmail }}
    <managingEditor>{{.}}{{ with $authorName }} ({{ . }}){{ end }}</managingEditor>{{ end }}{{ with $authorEmail }}
    <webMaster>{{ . }}{{ with $authorName }} ({{ . }}){{ 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 }}
    {{- range $pages }}
    <item>
      <title>{{ .Title }}</title>
      <link>{{ .Permalink }}</link>
      <pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
      {{- with $authorEmail }}<author>{{ . }}{{ with $authorName }} ({{ . }}){{ end }}</author>{{ end }}
      <guid>{{ .Permalink }}</guid>
      <description>{{ .Content | transform.XMLEscape | safeHTML }}</description>
    </item>
    {{- end }}
  </channel>
</rss>

Once done, your RSS feed should be advertising the full-text instead of the summary.


  1. Personally, I find the full-text RSS feed produced by Hugo much more readable in my RSS feed reader than the summary. If I was to switch to using summaries again, I would likely manually produce them. ↩︎