💾 Archived View for wilw.capsule.town › log › 2021-03-04-gatsby-rss.gmi captured on 2023-07-10 at 14:02:52. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-04-19)
-=-=-=-=-=-=-
RSS has had a bit of a resurgence [1] for personal websites and blogs in recent years, especially with the growing adoption of Small Web [2] and IndieWeb [3] ideologies.
Many static site generators - including Hugo [4], Jekyll [5], and Eleventy [6] - can easily support the automatic generation of RSS feeds at build time (either directly, or through plugins).
The same is true for Gatsby [7] - the framework currently used to build this static website - and the good news is that setting up one feed, or multiple ones for different categories, only takes a few minutes.
This article talks about RSS feeds for blogs (a typical use-case), but is also relevant for other notes, podcasts, or anything else that is published periodically to your Gatsby site.
In Gatsby, the typical blog set-up involves the blog entries in markdown format, and a template "page" [8], which is used to render the markdown blog posts.
You'll also probably have a "blog" page which lists or paginates your posts for visitors to find them, and a `createPages` function in your `gatsby-node.js` that generates the pages from the template and markdown.
All this sounds way more complicated than it is in practice, and there are lots of guides available [9] to help set this up.
At the very least, this article assumes you have blog posts written in a directory containing markdown for each post similar to the following:
--- date: "2021-03-04T22:17:00Z" title: "Easily set up discoverable RSS feeds on a Gatsby website" description: "How to set up multiple discoverable RSS feeds for your static Gatsby website." tags: [100daystooffload, technology, javascript] --- The post content starts here...
The metadata (frontmatter) doesn't need to be exactly as shown, but having useful metadata (e.g. tags) in-place helps make your feeds richer.
To create the feeds, we'll use a Gatsby plugin called `gatsby-plugin-feed` [10], which will do most of the heavy-lifting for us (as long as you have a blog in place structured similarly to the way described above).
First off, add the plugin as a dependency: `yarn add gatsby-plugin-feed`. I also recommend installing `moment` to help with formatting dates for the feed (as we'll see later): `yarn add moment`.
Next, you'll need to create some code in `gatsby-config.js`. If you have a blog already then you likely already have content in this file (e.g. `gatsby-source-filesystem` configuration). Your file probably looks a little like the following:
module.exports = { siteMetadata: { title: 'My Cool Website', siteUrl: 'https://my.cool.website', }, plugins: [ { resolve: 'gatsby-source-filesystem', options: { ... }, }, 'gatsby-plugin-react-helmet', ], };
Along with any other plugins you may have.
To create the feed we'll make use of a GraphQL query, and a function which will create a feed object. If we define these separately (as below), it will give us more flexibility later.
In the same file (`gatsby-config.js`), at the top, first `require` the `moment` library we installed earlier, define the query we'll use, and a function to create a feed object:
const moment = require('moment'); // Query for all blog posts ordered by filename (i.e. date) descending const rssPostQuery = ` { allMarkdownRemark( sort: { order: DESC, fields: [fileAbsolutePath] }, filter: { fields: { slug: { regex: "/blog/" } } } ) { edges { node { html fields { slug } frontmatter { title description date tags } } } } } `; const createRssPost = (edge, site) => { const { node } = edge; const { slug } = node.fields; return Object.assign({}, edge.node.frontmatter, { description: edge.node.description, date: moment.utc(`${node.frontmatter.date}`, 'YYYY/MM/DDTHH:mmZ').format(), url: site.siteMetadata.siteUrl + slug, guid: site.siteMetadata.siteUrl + slug, custom_elements: [{ "content:encoded": edge.node.html }], });; };
The `rssPostQuery` assumes your blog posts are rendered at `/blog/filename` in your built site. If not, then just change this value in the regex. Likewise, the `createRssPost` function assumes the dates in the frontmatter of your posts are formatted like `YYYY/MM/DDTHH:mmZ` - if not, just change this string to match your own format (I use UTC here as we're dealing with global audiences!).
Essentially, the GraphQL query string returns all markdown files ordered by descending filename (I title my blog posts by date, so this gives a reverse chronological ordering of posts, with the newest first), and gives us the post content, slug ("path"), and selected fields from the posts' frontmatters.
We use a regex in the query to discern between different types of markdown files. For example, you may have a collection of notes - also written in markdown - which we want to ignore for the purposes of creating an RSS feed for _just_ blog posts.
The `createRssPost` function (which we'll call later), accepts a markdown file (`edge`) and information about the website (`site`), and returns a fresh object representing this information to be eventually embedded in the feed.
The `guid` field is a globally-unique ID for this post on your blog and reader software will use this to, for example, determine if the user has already seen the post and should mark it as "read". Since all of my posts have a unique path ("slug"), I just use this for the ID.
Finally, we need to add a section to our `plugins` array to tell `gatsby-plugin-feed` how to build our feed using the query and function we created above. In the same file, make the following changes:
module.exports = { siteMetadata: { ... }, // omitted for brevity plugins: [ { resolve: 'gatsby-source-filesystem', options: { ... }, // omitted for brevity }, { // Add this object to your "plugins" array: resolve: 'gatsby-plugin-feed', options: { feeds: [ { serialize: ({ query: { site, allMarkdownRemark } }) => allMarkdownRemark.edges.map(e => createRssPost(e, site)), query: rssPostQuery, output: '/rss.xml;, title: 'My Cool Blog', description: 'All of my blog posts' }, ], }, }, ... ], };
The `gatsby-plugin-feed` plugin only runs when the site is actually _built_. If you have your Gatsby site running locally, just run `gatsby build` in a separate Terminal window and then navigate to `/rss.xml` on your local development website to view the feed.
The example configuration in the previous section creates a single feed containing all blog posts.
However, you may have noticed that the `feeds` attribute is an array; this means that the plugin can be used to create multiple feeds. I do exactly that on this website [11]: I have different feeds for different audiences (e.g. for technology, life, books, etc.).
Since we've already broken our code out into a separate query and function, it is easy to add new feeds by `filter`ing on the markdown edges before passing them to `map` in the `serialize` function.
If you modify the same file again (`gatsby-config.js`), you can create a feed for all of your posts that contain a tag named "technology" as follows:
... // omitted for brevity { resolve: 'gatsby-plugin-feed', options: { feeds: [ { ... }, // omitted for brevity { serialize: ({ query: { site, allMarkdownRemark } }) => allMarkdownRemark.edges.filter(e => { const tags = e.node.frontmatter.tags; return tags && tags.length > 0 && tags.indexOf('technology') > -1; }).map(e => createRssPost(e, site)), query: rssPostQuery, output: '/technology.xml', title: 'My Technology Blog', description: 'Posts in my blog tagged with "technology".' }, ], }, }, ...
This will create a new feed at `/technology.xml` containing these tech posts.
Since it's just plain old JavaScript, you can use any of the available information to craft a number of flexible feeds for your visitors to subscribe to. You can then list these feeds on a page on your site, like this one [12].
The `gatsby-plugin-feed` plugin has one more trick up its sleeve: without any extra work it will automatically inject the relevant `<link />` tags to your site's HTML at build-time to list the feeds that you have configured.
This means that your visitors just need to add your site's root URL (e.g. "https://my.cool.website") into their feed reader and it will suggest the available feeds to them.
The image above shows the Reeder macOS app [13] automatically listing the available feeds on my website after entering just the root URL for the site. Visitors can then just add the ones they want.