Will's avatar

⬅️ See more posts

Easily set up discoverable RSS feeds on a Gatsby website

4 March 2021 (7 minute read)

🔮 This post is also available via Gemini.

100daystooffload technology javascript

💯 100 Days to Offload

This article is one of a series of posts I have written for the 100 Days to Offload challenge. Disclaimer: The challenge focuses on writing frequency rather than quality, and so posts may not always be fully planned out!

View other posts in this series.

RSS has had a bit of a resurgence for personal websites and blogs in recent years, especially with the growing adoption of Small Web and IndieWeb ideologies.

Many static site generators - including Hugo, Jekyll, and Eleventy - can easily support the automatic generation of RSS feeds at build time (either directly, or through plugins).

The same is true for Gatsby - 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.

Your Gatsby blog structure

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”, 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 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.

Creating your feeds

To create the feeds, we’ll use a Gatsby plugin called gatsby-plugin-feed, 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.

Creating multiple feeds

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: 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 filtering 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.

Feed discovery

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.

A screenshot showing the Reeder app auto-listing my website&rsquo;s feeds

The image above shows the Reeder macOS app 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.

✉️ You can reply to this post via email.

📲 Subscribe to updates

If you would like to read more posts like this, then you can subscribe via RSS.