skip to main content

RSS Feeds, Hugo, and Lazy Image Loading

Where's my content? It's a tarp!

published icon  |  category icon webdesign

tags icon hugo rss

Full RSS Content in Hugo

Just a quick one I wanted to get out there in case you are, like me, using Hugo to power a blog. Apparently, in 2017, the default behavior changed from using the .Content to the .Summary variable in the default rss.xml.


Update, 12 March 2021: This article officially became obsolete! Just use loading="lazy" - a lot less complicated and already decent browser support. Yay for upgrades!


What’s the big deal? I had no idea, until I started using a proper RSS reader today - the open source NetNewsWire for Mac. This is what your RSS feed will look like:

My Apple M1 article in an RSS reader. Where's all the text?

The above screenshot might mislead you into thinking I simply captured only a part - I did not. That’s the .Summary, right there. Since reading all “the news” in one place sounds intriguing, and I’d like other visitors to enjoy my full blog posts in these tools too, I’d have to change the default behavior. That can be easily done by copy-pasting the default into layouts/_default/rss.xml and altering it to your liking - such as swapping .Summary for .Content.

However, that brings us to to problem number two.

Lazy loading and RSS Feeds

Recently, after trying to maximize my Lighthouse score, especially on performance levels, I implemented lazysizes, a simple solution to lazy load <img/> tags, thereby reducing the critical path for a single page to load. That requires a custom render-image.html (only available when using the Goldmark Markdown renderer) in _default/_markup/ that looks like this:

<figure>
    <a href="{{ .Destination | safeURL }}" class="lbox">
        <noscript>
            <img src="{{ .Destination | safeURL }}" {{ with .Text }} alt="{{ . }}"{{ end }} {{ with .Title}} title="{{ . }}"{{ end }}>
        </noscript>     
        <img class="lazyload" data-src="{{ .Destination | safeURL }}" {{ with .Text }} alt="{{ . }}"{{ end }} {{ with .Title}} title="{{ . }}"{{ end }}>
    </a>
    {{ with .Title }}
        <figcaption>{{ . }}</figcaption>
    {{ end }}
</figure>

Do not forget the <noscript/> tag in case JavaScript is disabled or the visitor will not see any images!1 Same goes for the RSS feed: your index.xml that NetNewsWire loads will see an image tag, but not a src attribute. To fix that, I replaced the content, like so:

{{ $lazyLoadImg := "<img class=\"lazyload\" data-src=" }}
{{ $eagerLoadImg := "<img src=" }}
{{ $content := .Content | replaceRE $lazyLoadImg $eagerLoadImg | safeHTML }}      
<description>
  {{ $content }}
  ]]>
</description>

I know the documentation mentions render-image.rss.xml as a separate RSS renderer, but it as reported before, it currently does not work (v0.79.1).

Update, 9 Jan. 2021: It seems that the RSS reader Feedly processes <noscript/> tags, resulting in two displayed images instead of one. Another replaceRE to replace the tag fixes that, although it’s starting to get messy…

I still wasn’t satisfied. Some blog posts use a “big image” (or “featured image” masthead), that is part of the article header, and is currently not shown in the RSS reader. After inspecting the RSS specifications, it seems that a <img/> tag in the description is the only way to do it (provided a CDATA wrapper is present), as opposed to twitter cards that have a dedicated tag for this. So, the above description tag was extended, and now looks like this:

<description>
  {{ `<![CDATA[ ` | safeHTML }}
  {{ if .Params.bigimg }}
    <p>
      <img hspace="5" src="{{ $baseurl }}bigimg/{{ .Params.bigimg }}"/>
    </p>
  {{ end }}

  {{ $content | safeHTML }}
  ]]>
</description>

Let’s inspect the changes with our RSS reader:

Yes, a featured image and the rest of the text!

Scrolling down also reveals properly loaded images, hooray! Do not forget to add feed metadata to the <header/> tag so that NetNewsWire can automatically detect the location of your RSS feed:

<link href="{{ .RelPermalink }}" rel="alternate" type="application/rss+xml" title="Brain Baking" />
<link href="{{ .RelPermalink }}" rel="feed" type="application/rss+xml" title="Brain Baking" />

Enjoy my RSS feeds!


  1. Try it your for yourself by disabling JS in your browser. Your blog should be accessible to anyone - no CSS, no JS, accessibility options - try to include everyone. ↩︎

I'm Wouter Groeneveld, a level 36 Brain Baker, and I love the smell of freshly baked thoughts (and bread) in the morning. I sometimes convince others to bake their brain (and bread) too.

If you found this article amusing and/or helpful, you can buy me a coffee - although I'm more of a tea fan myself. I also like to hear your feedback via Mastodon or e-mail. Thanks!