David Moll's blog

You can't downvote me here
Home Archive About me Tags Stats

100% - Creating a complete RSS Feed

A cat reading a newspaper

Notice: This blog is part of a series called "100%" in which I describe the steps I took to cover all cases a website can face and get the maximum out of what a feature can offer. You can find all articles in this series under the 100%-tag

It's no secret that I am a big fan of RDF Site Summary or Really Simple Syndication (RSS). This technology developed more than 24 years ago in 1999 is still being used today to give people an optional way of aggregrating articles they are interested in without seeing any advertisements or fearing beeing tracked. And while companies such as Google try with all their might to bring RSS feeds down to bring more people into their system there are still tons and tons of amazing feeds to read. In this blog I want to explain how I added mine to the list and how you can do this too.

Initial setup

This blog is powered by 11ty which luckily has a plugin that makes adding a RSS/Atom-feed a breeze. All you need is to install the plugin with

npm install @11ty/eleventy-plugin-rss --save-dev

and add it to your eleventy-config:

const pluginRss = require("@11ty/eleventy-plugin-rss");

module.exports = function(eleventyConfig) {

The last step to getting the basics setup is creating a new file in your root-directory called for example rss.njk and pasting into it the template provided on the page for the RSS-plugin. Now after building and serving the website you can find your feed by default under /feed.xml or feed.json if you chose the JSON-version. You can find for example mine under https://blog.davidmoll.net/feed.xml

Advertising your feed

By default RSS-readers do not know where your feed is collected and if it is for example an RSS or Atom-feed. You can add following snippet to your <head> so the readers know where to look for your feed:

      title="David Moll's RSS Feed"
      title="David Moll's Atom Feed"


The default template is good, but not perfect. Several tags are missing and while optional they should still be included to fully cover the RSS-specification:


<copyright> is self explanatory, it shows the copyright owner of the material you are reading. I have populated the tag with "Copyright 2024, David Moll".


<managingEditor> is the email adress for the person responsible for editorial content followed by the persons name in brackets, for example "[email protected] (David Moll)"


<webMaster> is similar to <managingEditor> but it instead covers not the content but is responsible for the technical site such as hosting. It works the same as <managingEditor>, the email adress followed by the name of the responsible person in brackets.


<pubDate> represents the publication date for the content in the channel. For example, the New York Times publishes on a daily basis, the publication date flips once every 24 hours. That's when the pubDate of the channel changes. All date-times in RSS conform to the Date and Time Specification of RFC 822, with the exception that the year may be expressed with two characters or four characters (four preferred). Getting that date was a little bit tricky but with a bit of smart thinking we can get that date really easily.

What we need is the collections.posts-Array which contains all blog-posts with the tag posts, grab only the last element of it, i.e. our newest post with .slice(-1)[0] (thanks to this post I found this neat one-liner), get the date with .date and throw it to the provided filter by the RSS-plugin | dateToRfc822 to format it in RFC822 which <pubdate> needs. So for me the complete line looks like

<pubDate>{{ collections.posts.slice(-1)[0].date | dateToRfc822 }}</pubDate>


<lastBuildDate is very similar to <pubDate>, but instead it displays the time when anything was changed on the channel for the last time. So what we are looking for is the last time this site was build, which can be done with a small filter that you can place in your eleventy-config:

	eleventyConfig.addGlobalData('lastBuildDate', () => {
		return (new Date).toUTCString();

Now all you need is to use that filter in the RSS-template like this:

<lastBuildDate>{{ lastBuildDate }}</lastBuildDate>


<category> is very simple, it specifies the category of the content in a channel, so for me this was "Blog".


<generator> is a string indicating the program used to generate the channel, so for me "11ty".


<docs> is a URL that points to the documentation for the format used in the RSS file. It's for people who might stumble across an RSS file on a Web server 25 years from now and wonder what it is. In basically every case this website is this one


<image> defines the image that gets shown as the icon of your RSS-feed together with the title and link to your website. This object can be build like this:

    <title>David Moll's blog</title>



RSS is not made to send an image as a description. Luckily for us some very smart people figured out that when you put an image together with the description into CDATA you have no problem displaying an image together with the text describing the post. For me this looks like this:

<![CDATA[<img align="left" hspace="5" src="{{metadata.url }}/assets/images/{{post.data.folderName}}/cover.png"/>{{ post.data.description }}]]>


author defines the email adress and name of the author of the item. It is formatted similar to <managingEditor> and <webMaster>, so all that is needed is

<author>{{ metadata.author.url }} ({{ metadata.author.name }})</author>

And thats it, you now have a complete RSS-feed with all bells and whistles that you can get validated by the RSSBoard or W3. If you want to copy my entire config you can copy it here: You can find the entire config here on Gitub.