Eleventy - Convert a RSS Feed to a collection

A section of a machine is exposed with a series of cogs showing. At the bottom is a RSS icon and at the top is a symbol for an array. It is cast againt a soft purple background with the eleventy possum floating beside it on a balloon.

I want to create an archive of my tech writing from across the web. One source is the author RSS feeds of blogs I wrote for. How about I transform it into an eleventy collection?

My use case - Creating an archive of my tech writing from across the web

This tutorial follows on from my previous eleventy tutorial where my objective was to create an archive of my tech writing from across the web. In this instance, I am creating a collection of the articles I wrote for LogRocket’s blog sourced from the author RSS feed for myself.

This is the LogRocket author page for rob oleary listing articles written for the blog. It features articles sorted from recent to least recent.
The author page for Rob O'Leary on LogRocket. It has a RSS feed for all articles written by moi!

I will list the LogRocket articles on the “Other Writing” page as per screenshot below to demonstrate using the collection.

The 'Other Writing' page for our demo website lists all of the articles written for the LogRocket blog sourced from the author RSS Feed for Rob OLeary

Without further ado, let’s walk through the code.

The code

This is an abbreviated view of the project structure:

.
├── content
│   ├── blog.njk
│   ├── other-writing.njk
│   └── posts
│       ├── post1.md
│       ├── post2.md
│       ├── post3.md
├── _data
│   └── rss.js
├── eleventy.config.js

The _data/rss.js file is the global data file that will do the fetching and transformation of the RSS feed into a collection.

The content/other-writing.njk file is the Nunjucks template for the “Other Writing” webpage.

Let’s look at the code for each of these.

The global data file - rss.js

Install the eleventy fetch eleventy plugin. We will use it to fetch the RSS feed.

Fetch the RSS feed weekly

The eleventy fetch takes 2 parameter: a URL and an options object. To fetch the LogRocket RSS feed once a week. I provide the property duration: 1w to the plugin’s options object.

The plugin can save any kind of asset — we choose “text” as the content type when it is not JSON or a media format. I provide the property type: "text" to the plugin’s options object.

Javascript
// excerpt from rss.js

let fetch = require("@11ty/eleventy-fetch");

module.exports = async function () {
let url = `https://blog.logrocket.com/author/rob-oleary/feed/`;
let response;

try {
response = await fetch(url, {
duration: "1w", // save for 1 week
type: "text",
});
} catch (error) {
console.error(`Fetch failed in rss.js. ${error}`);
}

// more code
}

Now we have the RSS feed saved and cached locally. Next we can manipulate the XML content of the feed.

Validate the XML and transform the XML to a JavaScript object

I looked through a few JavaScript XML libraries, I found the documentation was poor for most. And maybe its just me, but the APIs are odd and hard to figure out. I settled on fast-xml-parse. I found it was the easiest to figure out for my use cases. The library can validate XML and transform XML to a JavaScript object.

The following functions cover my use cases:

Here is the code for validating the XML and transforming it to a JavaScript Object:

Javascript
// excerpt from rss.js

let fetch = require("@11ty/eleventy-fetch");
let parser = require("fast-xml-parser");

module.exports = async function () {
// previous code

let feed;
const result = parser.XMLValidator.validate(response);

if (result === true) {
const xmlparser = new parser.XMLParser();
feed = xmlparser.parse(response);
} else {
console.error(
`rss.js - XML is invalid. Reason: ${result.err.msg}`
);
}
}

The object returned from the parse() function looks like this:

Javascript
{
"?xml": "",
"rss": {
"channel": {
"title": "Rob O'Leary, Author at LogRocket Blog",
"link": "https://blog.logrocket.com/author/rob-oleary/",
"description": "Resources to Help Product Teams Ship Amazing Digital Experiences",
"item": [
{
"title": "Using Prettier and ESLint for JavaScript formatting",
"link": "https://blog.logrocket.com/using-prettier-eslint-javascript-formatting/",
"dc:creator": "Rob O'Leary",
"pubDate": "Thu, 18 Apr 2024 16:00:14 +0000",
"description": "<p>Learn how to use ESLint and Prettier together to automatically format and fix JavaScript code in your projects.</p>\n<p>The post <a href=\"https://blog.logrocket.com/using-prettier-eslint-javascript-formatting/\">Using Prettier and ESLint for JavaScript formatting</a> appeared first on <a href=\"https://blog.logrocket.com\">LogRocket Blog</a>.</p>\n",
"content:encoded": "...long HTML content"
},
{
"title": "Using CSS content-visibility to boost your rendering performance",
"link": "https://blog.logrocket.com/using-css-content-visibility-boost-rendering-performance/",
"comments": "https://blog.logrocket.com/using-css-content-visibility-boost-rendering-performance/#respond",
"dc:creator": "Rob O'Leary",
"pubDate": "Wed, 08 Nov 2023 15:30:09 +0000",
"category": ["Uncategorized", "css"],
"guid": "https://blog.logrocket.com/?p=30286",
"description": "<p>CSS <code>content-visibility</code> helps boost rendering performance by controlling whether or not an element renders its contents. </p>\n<p>The post <a href=\"https://blog.logrocket.com/using-css-content-visibility-boost-rendering-performance/\">Using CSS content-visibility to boost your rendering performance</a> appeared first on <a href=\"https://blog.logrocket.com\">LogRocket Blog</a>.</p>\n",
"content:encoded": "...long HTML content"
},
// more items
]
}
}
}

Transform the articles to a custom collection

We can reference the array of articles through feed.rss.channel.item. Now it is just a case of transforming that array into a form that satisfies your usage.

One field you will want to manipulate is pubDate, it is a string and needs to be converted a Date object to be used a date. I find it is best to be consistent and follow the same structure as the collection item object that Eleventy uses internally. Your templates are consistent then.

Here I use the map() method of Array to create a new array with the fields transformed:

Javascript
// _data/rss.js
module.exports = async function () {
// previous code

let posts = feed.rss.channel.item;

let transformedPosts = posts.map((post) => {
let transformedPost = {};
transformedPost.date = new Date(post.pubDate);
transformedPost.url = post.link;
transformedPost.data = {};
transformedPost.data.title = post.title;
return transformedPost;
});

return transformedPosts;
};

That’s it. Now the data file will make our transformed posts available as a global collection that can be referenced as rss in our templates.

You can find the full code for the file at: https://github.com/robole/eleventy-tutorials/blob/main/rss-feed-to-collection/_data/rss.js.

Using the collection in a nunjucks template - other-writing.njk

Here I loop through the rss collection and generate an ordered list of posts for the “Other Writing” page.

Nunjucks
<section class="blog">
<p>This is a collection of posts...</p>

<ol>
{% for post in rss %}
<li class="post">
<h3><a href="{{ post.url }}">{{ post.data.title }}</a></h3>
<span>|</span>
<time>{{ post.date | simpleDate }}</time>
</li>
{% endfor %}
</ol>

</section>

This is how the page looks:

The 'Other Writing' page for our demo website listing all of the articles written for the LogRocket blog sourced from a RSS Feed

Source code

The source code is available in this GitHub repo – https://github.com/robole/eleventy-tutorials. You can find it as an independent project in the rss-feed-to-collection folder.

Can you give the repo a star to indicate that this was a worthwhile tutorial please? 🌟

Conclusion

If you want to transform a RSS feed and use it as a collection, you should be able to adapt this code with minimal effort. This method enabled me to create an archive of my tech writing from across the web. If I publish more articles on the LogRocket blog in future, they will be automatically picked from the RSS feed.

Tagged