- Stable
3.1.6- Canary
4.0.0-alpha.10
- Guide
Guide
- Get Started
- Command Line Usage
- Add a Configuration File
- Copy Files to Output
- Add CSS, JS, Fonts
- Importing Content
- Configure Templates with Data
- Using Data in Templates
- Template Languages
- Template Features
- Environment Variables
- Internationalization (i18n)
- Watch Files and Dev Servers
- Common Pitfalls
- Advanced
- Plugins
- Services
RSS Plugin
On this page
A pack of plugins for generating an RSS (or Atom or JSON) feed using the Nunjucks templating syntax.
Feature highlights
- URLs are normalized to absolute URLs, pointing to your hosted domain for maximum feed reader compatibility. Read more about the dangers of relative URLs in your feeds on CSS Tricks (Related: #36).
- Existing project Transforms are applied to feed entries (e.g. Image HTML Transform,
<base>, InputPath to URL, etc.) If you’re using a--pathprefixin your project, the URLs in your feed content are normalized for you.
Since RSS Plugin v2.0, this plugin offers two options to create feeds in your project:
- Virtual templates let you create a feed with a few lines of configuration. (This is easier, more abstracted.)
- Manual templates need more configuration and a template file. (This requires more setup, but offers more control.)
Installation
Available on npm.
npm install @11ty/eleventy-plugin-rss
v3of this plugin is ESM-only and requires Eleventy v3.0 or newer.v2of this plugin requires Eleventy v3.0 or newer.v1of this plugin is compatible with Eleventy 0.11 or newer.
Upgrading from v2
- Version 3 of this plugin is ESM and requires Eleventy v3 or newer. It does not require ESM projects or an ESM config file.
- CommonJS configuration files on Node 20.19+ must use
require("@11ty/eleventy-plugin-rss").default. Older versions of Node need dynamicimport(). - Breaking change in v3.0: The helper functions (
feedPlugin,dateToRfc3339,dateToRfc822,getNewestCollectionItemDate,absoluteUrl, andconvertHtmlToAbsoluteUrls) are only available as named exports (not as properties of the default export).
Virtual Template
Added in v3.0.0Added in RSS 2.0.0 This method creates a feed template directly from your plugin configuration, without requiring additional files in your project. The default template uses Nunjucks, so make sure njk is included templateFormats in your Eleventy config. Read more about Virtual Templates.
require("@11ty/eleventy-plugin-rss").default. On older versions of Node (< 20.19), you’ll need to use dynamic import() instead of require.
import { feedPlugin } from "@11ty/eleventy-plugin-rss";
export default function (eleventyConfig) {
eleventyConfig.addPlugin(feedPlugin, {
type: "atom", // or "rss", "json"
outputPath: "/feed.xml",
collection: {
name: "posts", // iterate over `collections.posts`
limit: 10, // 0 means no limit
sort: "auto", // or "descending", "ascending"
},
metadata: {
language: "en",
title: "Blog Title",
subtitle: "This is a longer description about your blog.",
base: "https://example.com/",
author: {
name: "Your Name",
email: "", // Optional
}
}
});
};
const { feedPlugin } = require("@11ty/eleventy-plugin-rss");
module.exports = function (eleventyConfig) {
eleventyConfig.addPlugin(feedPlugin, {
type: "atom", // or "rss", "json"
outputPath: "/feed.xml",
collection: {
name: "posts", // iterate over `collections.posts`
limit: 10, // 0 means no limit
sort: "auto", // or "descending", "ascending"
},
metadata: {
language: "en",
title: "Blog Title",
subtitle: "This is a longer description about your blog.",
base: "https://example.com/",
author: {
name: "Your Name",
email: "", // Optional
}
}
});
};
This configuration is the only step for the Virtual Template method. If you need additional control over the template output, you can use the Manual Template method.
Expand for full options list
type: (optional) One of"atom"(default),"rss", or"json"outputPath: (required, default:/feed.xml) Where to write the template in the output directory.inputPath: (optional, default based onmetadata.title) Change where the virtual template pretends to live on the file system (e.g. if you want project directory data files to apply via the Data Cascade)collection.name: Collection entries to iterate over to populate your feed (e.g.name: "posts"forcollections.posts)collection.limit: Number of entries to include. (Use0for no limit.)collection.sort: Added in RSS 3.1.0 (optional) Order of feed entries, applied beforecollection.limit. Any other value throws an error."auto"(default): Reverses the collection, assuming Eleventy’s default oldest-first order."descending": Sorts bydate, newest first, regardless of the collection’s order. Use this for custom collections already sorted newest-first, which otherwise produce a feed of your oldest posts."ascending": Sorts bydate, oldest first.
metadata: Content used to populate the feed boilerplate.metadata.subtitleis used for the feed description.metadata.icon,metadata.logo: Added in RSS 3.0.0 (optional) URLs to a feed icon and logo.stylesheet: URL to an XSL stylesheet to change how the feed is rendered in the browser (only for Atom and RSS feeds).script: Added in RSS 3.0.0 URL to an XSLT polyfill script, injected into Atom and RSS feeds.templateData, defaults to{}: Additional data to apply to the template (e.g. to add your feed to the Navigation plugin)
Added in RSS 3.0.0 Use summary in a post’s data (post.data.summary) to populate the entry <summary> (Atom) or <description> (RSS).
Virtual Template with addTemplate
Added in RSS 3.1.0 getFeedTemplate returns the content and data for the same feed feedPlugin creates, for use with $config.addTemplate. This unlocks unlimited feeds of the same type. The feed template uses filters from the main plugin, make sure you add the plugin (via rssPlugin) first.
import { rssPlugin, getFeedTemplate } from "@11ty/eleventy-plugin-rss";
export default function ($config) {
$config.addPlugin(rssPlugin);
let { content, data } = getFeedTemplate({
type: "atom", // or "rss", "json"
outputPath: "/posts.xml",
collection: {
name: "posts",
limit: 10,
},
metadata: {
language: "en",
title: "Blog Title",
subtitle: "This is a longer description about your blog.",
base: "https://example.com/",
author: {
name: "Your Name",
},
},
});
$config.addTemplate("feeds/posts.njk", content, data);
};
const { rssPlugin, getFeedTemplate } = require("@11ty/eleventy-plugin-rss");
module.exports = function ($config) {
$config.addPlugin(rssPlugin);
let { content, data } = getFeedTemplate({
type: "atom", // or "rss", "json"
outputPath: "/posts.xml",
collection: {
name: "posts",
limit: 10,
},
metadata: {
language: "en",
title: "Blog Title",
subtitle: "This is a longer description about your blog.",
base: "https://example.com/",
author: {
name: "Your Name",
},
},
});
$config.addTemplate("feeds/posts.njk", content, data);
};
getFeedTemplate accepts the same options as the Virtual Template method, except inputPath: pass the path as the first argument to addTemplate instead. You can extend data before passing it in, e.g. { ...data, eleventyNavigation: { key: "Feed" } }.
Manual Template
Configuration
Open up your Eleventy config file (probably eleventy.config.js) and use addPlugin:
import { rssPlugin } from "@11ty/eleventy-plugin-rss";
export default function (eleventyConfig) {
eleventyConfig.addPlugin(rssPlugin);
};
const { rssPlugin } = require("@11ty/eleventy-plugin-rss");
module.exports = function (eleventyConfig) {
eleventyConfig.addPlugin(rssPlugin);
};
require("@11ty/eleventy-plugin-rss").default. On older versions of Node (< 20.19), you’ll need to use dynamic import() instead of require.
Expand to see full options list
Added in RSS 1.1.0 Advanced control of PostHTML rendering options via posthtmlRenderOptions.
import { rssPlugin } from "@11ty/eleventy-plugin-rss";
export default function (eleventyConfig) {
eleventyConfig.addPlugin(rssPlugin, {
posthtmlRenderOptions: {
closingSingleTag: "default", // opt-out of <img/>-style XHTML single tags
},
});
};
const { rssPlugin } = require("@11ty/eleventy-plugin-rss");
module.exports = function (eleventyConfig) {
eleventyConfig.addPlugin(rssPlugin, {
posthtmlRenderOptions: {
closingSingleTag: "default", // opt-out of <img/>-style XHTML single tags
},
});
};
Supplies the following Nunjucks Filters
getNewestCollectionItemDate: Gets the most recently updated content in the collection. Use withdateToRfc3339to properly format the Date for the top-level<updated>element. Added in RSS 1.1.0dateToRfc3339: format a Date for use in a<entry><updated>element. (Atom feeds) Added in RSS 1.1.0dateToRfc822: format a Date for use in a<pubDate>element. (RSS feeds) Added in RSS 1.2.0
Less Important Filters
- Deprecated
absoluteUrl: For performance reasons, therenderTransformsfilter is recommended instead, making use of the HTML<base>plugin. Converts a single URL (relative or absolute path) to a full absolute URL including protocol, domain, full path. - Deprecated
htmlToAbsoluteUrls: For performance reasons, therenderTransformsfilter is recommended instead, making use of the HTML<base>plugin. (async) Transforms all of the URLs in a block of HTML withabsoluteUrlabove. Uses posthtml-urls witha[href],video[src],audio[src],source,img[src],[srcset]and a whole bunch more. - ⚠️ Removed in RSS v2.0.0
rssLastUpdatedDate, poorly named (works with Atom and JSON feeds, not RSS). UsegetNewestCollectionItemDate | dateToRfc3339instead. - ⚠️ Removed in RSS v2.0.0
rssDate, poorly named (works with Atom and JSON feeds, not RSS). UsedateToRfc3339instead.
Use with other template languages
Added in RSS 1.1.0 This plugin exports dateToRfc3339, dateToRfc822 (Added in RSS 1.2.0), getNewestCollectionItemDate, absoluteUrl, and convertHtmlToAbsoluteUrls functions so you can use with your own filters. For example:
import { dateToRfc3339, dateToRfc822 } from "@11ty/eleventy-plugin-rss";
export default function ($config) {
$config.addLiquidFilter("dateToRfc3339", dateToRfc3339);
// New in RSS 1.2.0
$config.addLiquidFilter("dateToRfc822", dateToRfc822);
};
const { dateToRfc3339, dateToRfc822 } = require("@11ty/eleventy-plugin-rss");
module.exports = function ($config) {
$config.addLiquidFilter("dateToRfc3339", dateToRfc3339);
// New in RSS 1.2.0
$config.addLiquidFilter("dateToRfc822", dateToRfc822);
};
html-entities).Sample Feed templates
Copy and paste this template and modify the JSON metadata to match your feed’s needs. Make sure collections.posts matches the template collection you want to provide a feed for.
---json
{
"permalink": "feed.xml",
"eleventyExcludeFromCollections": true,
"metadata": {
"title": "My Blog about Boats",
"description": "I am writing about my experiences as a naval navel-gazer.",
"language": "en",
"base": "https://example.com/",
"author": {
"name": "Boaty McBoatFace"
}
}
}
---
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="{{ metadata.language or page.lang }}">
<title>{{ metadata.title }}</title>
<subtitle>{{ metadata.description }}</subtitle>
<link href="{{ permalink | htmlBaseUrl(metadata.base) }}" rel="self" />
<link href="{{ metadata.base | addPathPrefixToFullUrl }}" />
<updated>{{ collections.posts | getNewestCollectionItemDate | dateToRfc3339 }}</updated>
<id>{{ metadata.base | addPathPrefixToFullUrl }}</id>
<author>
<name>{{ metadata.author.name }}</name>
</author>
{%- for post in collections.posts | reverse %}
{%- set absolutePostUrl %}{{ post.url | htmlBaseUrl(metadata.base) }}{% endset %}
<entry>
<title>{{ post.data.title }}</title>
<link href="{{ absolutePostUrl }}" />
<updated>{{ post.date | dateToRfc3339 }}</updated>
<id>{{ absolutePostUrl }}</id>
<content type="html">{{ post.content | renderTransforms(post.data.page, metadata.base) }}</content>
</entry>
{%- endfor %}
</feed>
---json
{
"permalink": "feed.xml",
"eleventyExcludeFromCollections": true,
"metadata": {
"title": "My Blog about Boats",
"description": "I am writing about my experiences as a naval navel-gazer.",
"language": "en",
"base": "https://example.com/",
"author": {
"name": "Boaty McBoatFace"
}
}
}
---
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:base="{{ metadata.base | addPathPrefixToFullUrl }}" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>{{ metadata.title }}</title>
<link>{{ metadata.base | addPathPrefixToFullUrl }}</link>
<atom:link href="{{ permalink | htmlBaseUrl(metadata.base) }}" rel="self" type="application/rss+xml" />
<description>{{ metadata.description }}</description>
<language>{{ metadata.language or page.lang }}</language>
{%- for post in collections.posts | reverse %}
{%- set absolutePostUrl = post.url | htmlBaseUrl(metadata.base) %}
<item>
<title>{{ post.data.title }}</title>
<link>{{ absolutePostUrl }}</link>
<description>{{ post.content | renderTransforms(post.data.page, metadata.base) }}</description>
<pubDate>{{ post.date | dateToRfc822 }}</pubDate>
<dc:creator>{{ metadata.author.name }}</dc:creator>
<guid>{{ absolutePostUrl }}</guid>
</item>
{%- endfor %}
</channel>
</rss>
---json
{
"permalink": "feed.json",
"eleventyExcludeFromCollections": true,
"metadata": {
"title": "My Blog about Boats",
"description": "I am writing about my experiences as a naval navel-gazer.",
"language": "en",
"base": "https://example.com/",
"author": {
"name": "Boaty McBoatFace"
}
}
}
---
{
"version": "https://jsonfeed.org/version/1.1",
"title": "{{ metadata.title }}",
"language": "{{ metadata.language or page.lang }}",
"home_page_url": "{{ metadata.base | addPathPrefixToFullUrl }}",
"feed_url": "{{ permalink | htmlBaseUrl(metadata.base) }}",
"description": "{{ metadata.description }}",
"authors": [
{
"name": "{{ metadata.author.name }}"
}
],
"items": [
{%- for post in collections.posts | reverse %}
{%- set absolutePostUrl %}{{ post.url | htmlBaseUrl(metadata.base) }}{% endset %}
{
"id": "{{ absolutePostUrl }}",
"url": "{{ absolutePostUrl }}",
"title": "{{ post.data.title }}",
"content_html": {% if post.content %}{{ post.content | renderTransforms(post.data.page, metadata.base) | dump | safe }}{% else %}""{% endif %},
"date_published": "{{ post.date | dateToRfc3339 }}"
}
{% if not loop.last %},{% endif %}
{%- endfor %}
]
}
Place the file in your input directory, and give it a .njk extension. (For example: src/feed.njk or src/feed.json.) If your input directory is src, the file will be transformed into a feed.xml (or feed.json if you’re using the JSON variant) file at the root of your website when Eleventy builds. It can then be useful to check the file against a feed validator, such as the W3C Feed Validation Service to make sure that the output was good.
Ultimately, your feed will be available at https://yourwebsite.com/feed.xml (or https://yourwebsite.com/feed.json).
Community Plugins
eleventy-xml-pluginfor Liquid.