Configuring RSS Feeds in Grav CMS using Collections
While the Feed plugin for Grav CMS provides RSS, Atom and JSON feeds for your site content, setting up a feed in Grav differs from other content management systems because it requires explicit configuration rather than automatic discovery. The key to making feeds work lies in understanding how Grav uses collections to organise and present content.
Understanding Collections
In Grav, content lives in a file tree structure. The feed system works through collections, which tell Grav where to find pages and how to organise them. Collections are essential for feeds because they define exactly which content gets syndicated. Without a properly configured collection, your feed will be empty.
Collections use a specific syntax to target pages in different ways:
| Collection Type | Syntax | What It Does |
|---|---|---|
| All descendants | '@page.descendants': '/blog' |
Gathers a folder and all nested subdirectories recursively |
| Direct children only | '@page.children': '/blog' |
Limits collection to immediate children, excluding deeper nesting |
| By tag | '@taxonomy.tag': 'featured' |
Pulls all pages with a specific tag, regardless of location |
| By category | '@taxonomy.category': 'news' |
Pulls all pages in a specific category, regardless of location |
| Multiple sources | Array of any above | Merges content from multiple folders or taxonomies |
The @page.descendants syntax works well for blog structures where you might have posts organised by year, month or category within subdirectories. The @page.children syntax suits flatter structures where all posts sit directly under a single parent folder.
Taxonomy-based collections using @taxonomy.tag or @taxonomy.category pull pages based on their metadata rather than folder location. This allows you to create feeds for specific topics regardless of where those pages live in your file structure.
The real power emerges when combining multiple collection sources. You can specify an array of different folders or taxonomies, and Grav merges them into a single unified collection. This allows you to aggregate content across your entire site structure, pulling from blogs, articles, news sections or any combination that suits your needs.
Understanding Routes
Before configuring your feed, you need to understand how Grav translates folder paths into routes. This matters because you will reference routes in your collection configuration, not physical folder paths. Getting this wrong means your feed will be empty even when your folders contain content.
Grav stores pages in folders under /user/pages/, but these physical paths differ from the routes you use in configuration. Routes are based on folder names without numeric prefixes:
| Physical Path | Route in Configuration |
|---|---|
/user/pages/03.deliberations/ |
/deliberations |
/user/pages/writings/ |
/writings |
The numeric prefix (such as 03.) determines ordering in navigation but does not appear in the route. This means when you create a folder named 03.deliberations, Grav automatically makes it accessible at the /deliberations route. Similarly, a folder without a numeric prefix like writings becomes the /writings route.
When you specify '@page.descendants': '/deliberations' in your collection configuration, Grav knows to look in the 03.deliberations folder and all its subdirectories. The route system abstracts away the physical folder structure, allowing you to reorganise content by changing numeric prefixes without breaking your feed configuration.
Creating Your Feed Configuration
Creating a feed requires a page that defines which content to include. The simplest approach is to create a file at feed/default.md with a structure like this:
---
title: Site Feed
visible: false
content:
items:
'@page.descendants': '/blog'
order:
by: date
dir: desc
limit: 20
feed:
title: "My Site Feed"
description: "Latest posts and updates"
---
Configuration Settings Explained
The visible: false setting ensures the feed page does not appear in navigation.
The content section defines your collection. Here, @page.descendants: '/blog' recursively includes all pages under the /blog folder, including any nested within subdirectories. If you only want immediate children, use @page.children: '/blog' instead.
The order section determines how posts appear in your feed. The example uses by: date with dir: desc to show the newest posts first. Ensure all pages in your collection have a date: field in their front matter:
---
title: My Post
date: 16-02-2026 14:30
---
Alternatively, you can order by folder name using by: folder. This sorts pages alphabetically and proves reliable when pages lack dates or when folder structure determines chronology:
order:
by: folder
dir: asc
The limit: 20 setting controls how many items appear in the feed.
The feed section provides metadata for the RSS feed itself, including the title and description that feed readers will display.
Combining Multiple Folders
You can pull content from multiple locations into a single feed:
content:
items:
- '@page.descendants': '/blog'
- '@page.descendants': '/articles'
- '@page.descendants': '/news'
This merges posts from all three sections into one unified feed. Each section is gathered separately and then combined.
Putting It All Together
Here is a complete working configuration that demonstrates these principles in practice. This example pulls all content from a single folder using folder-based ordering rather than dates:
---
title: Surroundings Feed
visible: false
content:
items:
'@page.descendants': '/deliberations'
order:
by: folder
dir: asc
limit: 20
feed:
title: "Varied Surroundings, Deliberations"
description: "Essays and reflections from Deliberations on Assorted Explorations"
limit: 20
---
This configuration, saved as feed/default.md, pulls all content from the /deliberations folder and its subdirectories. The folder-based ordering uses Grav's numeric prefix system for sorting. Pages in folders named 01.first-post, 02.second-post, 03.third-post will appear in that sequence. The ascending direction means lower numbers appear first in the feed. The feed metadata provides a descriptive title and description that will appear in feed readers.
What the Output Looks Like
Once Grav processes your configuration, it generates an RSS XML file. Here is what the header section looks like using the real-world example above:
<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<channel>
<title>Varied Surroundings, Deliberations</title>
<link>https://www.assortedexplorations.com/surroundings/feed</link>
<description>Essays and reflections from Deliberations on Assorted Explorations</description>
<language>en</language>
<lastBuildDate>Sun, 25 Jan 2026 18:16:13 +0000</lastBuildDate>
Each post appears as an item with its title, link, publication date and content excerpt:
<item>
<title>Nation or Nations?</title>
<link>https://www.assortedexplorations.com/surroundings/deliberations/nation-or-nations</link>
<guid>https://www.assortedexplorations.com/surroundings/deliberations/nation-or-nations</guid>
<pubDate>Sun, 25 Jan 2026 17:59:23 +0000</pubDate>
<description>
<![CDATA[
<h2 class="centre mb-5">Nation or Nations?</h2>
<p style="text-align:center; margin-bottom: 2.5rem;"><img src="../images/51.jpg"></p>
<p>The nature of the United Kingdom seems to be a source not only of some confusion...</p>
]]>
</description>
</item>
The Feed plugin handles all XML generation automatically. You simply configure which content to include and how to order it, and Grav produces properly formatted RSS, Atom or JSON feeds.
Accessing Your Feeds
Once configured, your feeds become accessible at /feed.rss, /feed.atom or /feed.json. While this guide focuses on RSS feeds, the collection principles apply much more broadly. Collections power archive pages, tag pages, search results, related posts and sitemaps. Understanding how they work is essential for any feature that lists content on your site. The official documentation provides additional detail on these concepts and advanced configuration options.
Please be aware that comment moderation is enabled and may delay the appearance of your contribution.