How to Add an RSS Feed to Your Gatsby Site
Tags: gatsby, rss, webdev, tutorial
Published: 2021-02-03 08:51:32

## Introduction

As a content creator, you want to make it easy for your readers to stay updated on your latest content. By adding an [RSS feed](https://www.rssboard.org/rss-specification) to your site, readers can get automatic updates whenever you post a new article, video, or photo of your dog.

In this post, I'll show you how to add an RSS feed to a Gatsby site.

## A Quick Crash Course on RSS Feeds

### How does an RSS feed work?

RSS stands for "really simple syndication." In other words, it's a way to give other websites information about your content so that they can get it in front of more users.

Here's a brief overview of how it works:

1. Your site generates an RSS feed that includes information about all your content. You add a link to it someplace (maybe your site's header or footer) where it's easy for users to find.
1. A reader decides they love your content and want to get updates from your site. (Hooray!) They add the URL for your RSS feed to whatever **feed aggregator** software they use (like Feedly). The feed aggregator periodically checks your RSS feed to see if there's any new content.
1. Time goes by. When you post a new piece of content, your RSS feed gets updated to include additional data about the new content.
1. The next time your reader's feed aggregator checks your site's RSS feed, it sees the new content and notifies the reader. The reader checks out your new content and marvels at how handy it is that your site has an RSS feed!

The best part: once you set up your RSS feed the first time, all the updates happen automatically! 🥳

### What does an RSS feed actually look like?

An RSS feed is a specific kind of [XML](https://developer.mozilla.org/en-US/docs/Web/XML/XML_introduction) file. The structure looks similar to HTML, but it uses different kinds of elements.

The two main RSS elements are:

* `<channel>` - Contains information about your overall site, like the URL where it's hosted. Each RSS feed contains a single `<channel>` element, which itself contains one or more `<item>` child elements.
* `<item>` - Contains information about a single unit of content on your site, like a specific blog post or video. You'll have multiple `<item>` elements - one for each piece of content on your site.

> Looking for more details about the structure of an RSS feed? Here's the formal [RSS specification](https://www.rssboard.org/rss-specification).


## Prerequisites

In this post, you'll learn specifically how to add an RSS feed to an existing Gatsby site.

If you don't have a Gatsby site of your own, that's fine! I created some starter code that you can use to follow along. (Don't worry, I won't tell.) More on that in the next section.

If you'd rather follow along with your own Gatsby site, it will be helpful to have a basic blog set up, with separate pages for each post. The starter code uses the `gatsby-transformer-remark` plugin to query for Markdown nodes, but if your blog posts use something besides Markdown, you can make adjustments to the GraphQL queries specified in this post.

## The Starter Code

The starter code for this post is a simplified Gatsby blog. Posts are written in Markdown and are stored in the `src/blog` directory.

To get the site up and running locally:

1. Fork the [starter project repo](https://github.com/meganesu/gatsby-blog-with-rss-feed-example) on GitHub.
1. Clone your forked repo. (Don't forget to swap out `<your-github-username>` for your own GitHub username.)
    ```shell
    git clone https://github.com/<your-github-username>/gatsby-blog-with-rss-feed-example.git
    ```
1. In your terminal, change directories into the repo you just cloned.
    ```shell
    cd gatsby-blog-with-rss-feed-example
    ```
1. Start up the local development server.
    ```shell
    npm run develop
    ```
1. In a web browser, navigate to `localhost:8000`. You should see something like this:
    <img src="https://meganesulli.com/static/3ed41aa01260adb874deb3e005439ed2/a9965/blog-landing-page.png" alt='A blog landing page. The title says "My Blog Posts", and links to three posts are listed: "My First Post," "Another Great Post," and "Yet Another Post."' />

> A few notes on the starter code, if you're curious how it works:
>
> * It uses [Gatsby's File System Route API](https://www.gatsbyjs.com/docs/reference/routing/file-system-route-api/) to programatically generate new pages for each blog post. The template for each page is in the `src/pages/{MarkdownRemark.frontmatter__title}.js` file.
> * It also uses the [`gatsbyPath` field](https://www.gatsbyjs.com/docs/reference/routing/file-system-route-api/#routing-and-linking) from the File System Route API to generate slugs for each blog post page that's created.


## Adding an RSS Feed

The high-level process for adding an RSS feed to your Gatsby site looks like this:

* **Prerequisite:** Confirm that you have `siteMetadata` set up in your `gatsby-config.js` file.
* **Step 1:** Install the `gatsby-plugin-feed` plugin.
* **Step 2:** Configure `gatsby-plugin-feed` in your `gatsby-config.js` file.
* **Step 3:** Build and serve your site locally to test the RSS feed.
* **Step 4:** (Optional) Add a link to your RSS feed to your site.

> To skip ahead and see the finished product, check out the [`solution` branch](https://github.com/meganesu/gatsby-blog-with-rss-feed-example/tree/solution) of the starter code repo.

### Prerequisite: Confirm that you have `siteMetadata`

If you haven't already, you'll need to add a `siteMetadata` object to your `gatsby-config.js` file. This object contains high-level information about your site.

Your `siteMetadata` object will need the following properties:

- `title` - The name of your site.
- `siteUrl` - The URL where your site will be hosted once it's deployed.
- `description` - A short sentence describing the purpose of your site.

This data will be added to the `<channel>` element in your RSS feed.

The starter code shows what this looks like in context:

```javascript
// gatsby-config.js
module.exports = {
  siteMetadata: {
    title: "My Personal Blog",
    siteUrl: "https://my-personal-blog.com",
    description:
      "An example Gatsby blog with an automatically generated RSS feed.",
  },
  // ...
}
```

### Step 1: Install `gatsby-plugin-feed`

In your command line, install `gatsby-plugin-feed`.

```shell
npm install gatsby-plugin-feed
```

This will add the plugin to your `package.json` file as a dependency and add it to your `node_modules` folder.


### Step 2: Configure the plugin in your `gatsby-config.js` file

To make your Gatsby site actually use the plugin, you need to add it to the `plugins` array in your `gatsby-config.js` file. (You can add it before or after the existing plugins; it doesn't matter.)

```javascript
// gatsby-config.js
module.exports = {
  siteMetadata: { /* ... */ },
  plugins: [
    {
      resolve: 'gatsby-plugin-feed',
      options: {
        // We'll get to this in a moment
      }
    },
    // The rest of the existing plugins
  ]
}
```

Next, configure the additional options that `gatsby-plugin-feed` needs to generate your RSS feed. Your `options` object should have the following fields:

* `query` - The GraphQL query for pulling in data that should be shared across all the RSS feeds generated.
    * By default, this will pull in the data from `siteMetadata`. It's still a good idea to configure the query yourself, so that it's explicit.
* `feeds` - An array of feed objects (described below).
    * Unless you want to have separate RSS feeds for different kinds of content, you'll probably only need one feed object in this array.

A feed object has the following properties:

* `title` - (string) The text to put in the `<title>` element of the generated RSS feed.
* `output` - (string) The path where you want your generated RSS feed to be hosted on your site.
* `query` - (string) A GraphQL query for how to get the data for the contents of the feed.
* `serialize` - (function) A function specifying how to set properties on the `<item>` elements in the RSS feed for each piece of content.

Setting the `title` and `output` options are more straightforward, but the `query` and `serialize` options are described in more detail below.

#### Configuring `options.query` and `feed.query`

You may have noticed that there are two different `query` attributes in the plugin configuration: one under `options` and one in an individual feed object. Why are there two?

Imagine if you wanted to generate multiple RSS feeds for your site. For example, maybe you want one RSS feed for your blog posts and a separate RSS feed for your videos. Some of the data for your RSS feed will be the same in both cases (like the metadata for your site), but other data will be different (like the content of each `<item>` element in your feed).

The data from `options.query` will be included in all of your RSS feeds, while the data from the `query` field on a feed object will only be used for that specific feed.

Now let's put this into practice!

The query to pull in your shared site metadata will look like this:

```javascript
`
{
  site {
    siteMetadata {
      title
      description
      siteUrl
      site_url: siteUrl
    }
  }
}
`
```

And the query to pull in your blog data for your feed will look like this:

```javascript
`
{
  allMarkdownRemark(sort: {fields: frontmatter___date, order: ASC}) {
    nodes {
      frontmatter {
        title
        date
        description
      }
      html
      slug: gatsbyPath(filePath: "/{MarkdownRemark.frontmatter__title}")
    }
  }
}
`
```

> If you're new to GraphQL, you can start up your site in develop mode (with `npm run develop`) and use the GraphiQL explorer at `localhost:8000/___graphql` to see what queries and data fields you have available to you.

#### Configuring `serialize`

The `serialize` option takes a function that shows how to use the data from your GraphQL queries to build up the fields in each `<item>` element in the feed.

For this project, your `serialize` function will set the following properties for each item:

* `title` - The name of this piece of content.
* `description` - (optional) A 1-2 sentence summary of this piece of content.
* `url` - The full URL where this piece of content lives.
    * This will show up as a `<link>` element in the `<item>` element generated for the RSS feed.
* `guid` - Globally unique identifier. A string that is unique to this piece of content. (You can just use the URL again for this.)
* `date` - The publish date for this piece of content.
    * This will show up as a `<pubDate>` element in the `<item>` element generated for the RSS feed.

> To see the full list of all the additional item options you could pass in, refer to the [`rss` package documentation](https://www.npmjs.com/package/rss#itemoptions). The `rss` package is what `gatsby-plugin-feed` uses under the hood.

`serialize` receives all the data from the combined `options.query` and `feed.query` response. You can destructure this data and use it in your configuration for each `<item>` element. For example, the function below will create an `<item>` element with the `title`, `description`, and `date` data from `node.frontmatter`, and it also adds a `url` and `guid` property by combining data from the `site` and `allMarkdownRemark` queries.

```javascript
({ query: { site, allMarkdownRemark } }) => {
  return allMarkdownRemark.nodes.map(node => {
    return Object.assign({}, node.frontmatter, {
      url: `${site.siteMetadata.siteUrl}${node.slug}`,
      guid: `${site.siteMetadata.siteUrl}${node.slug}`,
    })
  })
}
```

#### The Full Plugin Configuration

After adding all the plugin options to your `gatsby-config.js` file, the configuration for `gatsby-plugin-feed` should look like this:

```javascript
// gatsby-config.js
{
  resolve: "gatsby-plugin-feed",
  options: {
    query: `
      {
        site {
          siteMetadata {
            title
            description
            siteUrl
            site_url: siteUrl
          }
        }
      }
    `,
    feeds: [
      {
        title: "My Personal Blog RSS Feed",
        output: "rss.xml",
        query: `
        {
          allMarkdownRemark(sort: {fields: frontmatter___date, order: ASC}) {
            nodes {
              frontmatter {
                title
                date
                description
              }
              html
              slug: gatsbyPath(filePath: "/{MarkdownRemark.frontmatter__title}")
            }
          }
        }
        `,
        serialize: ({ query: { site, allMarkdownRemark } }) => {
          return allMarkdownRemark.nodes.map(node => {
            return Object.assign({}, node.frontmatter, {
              url: `${site.siteMetadata.siteUrl}${node.slug}`,
              guid: `${site.siteMetadata.siteUrl}${node.slug}`,
            })
          })
        },
      }
    ]
  }
},
```

### Step 3: Build and serve your site locally to test the RSS feed

`gatsby-plugin-feed` only generates an RSS feed for production-style builds. (In other words, the URL to your RSS feed won't work if you're running `npm run develop`. You'll get a 404 error.)

To test your RSS feed locally, you'll have to build your site and start up a server by running the following command:

```shell
npm run build && npm run serve
```

Once your site is up and running, you should be able to see it in a web browser at `localhost:9000`. You can check your RSS feed by visiting `localhost:9000/rss.xml` (or whatever you configured the `output` option to be).

The generated RSS feed should look something like this:

```xml
<rss version="2.0">
  <channel>
    <title>My Personal Blog RSS Feed</title>
    <description>
      An example Gatsby blog with an automatically generated RSS feed.
    </description>
    <link>https://my-personal-blog.com</link>
    <generator>GatsbyJS</generator>
    <lastBuildDate>Wed, 27 Jan 2021 08:07:58 GMT</lastBuildDate>
    <item>
      <title>My First Post</title>
      <description>The first thing I've ever written.</description>
      <link>https://my-personal-blog.com/my-first-post/</link>
      <guid isPermaLink="false">https://my-personal-blog.com/my-first-post/</guid>
      <pubDate>Fri, 01 Jan 2021 00:00:00 GMT</pubDate>
    </item>
    <item>
      <title>Another Great Post</title>
      <description>The second thing I've ever written.</description>
      <link>https://my-personal-blog.com/another-great-post/</link>
      <guid isPermaLink="false">https://my-personal-blog.com/another-great-post/</guid>
      <pubDate>Sat, 02 Jan 2021 00:00:00 GMT</pubDate>
    </item>
    <item>
      <title>Yet Another Post</title>
      <description>The third thing I've ever written. I'm on a roll!</description>
      <link>https://my-personal-blog.com/yet-another-post/</link>
      <guid isPermaLink="false">https://my-personal-blog.com/yet-another-post/</guid>
      <pubDate>Sun, 03 Jan 2021 00:00:00 GMT</pubDate>
    </item>
  </channel>
</rss>
```

If it worked, congratulations, your site now has an automatically generated RSS feed! If it didn't, try checking out the [Troubleshooting Errors](#troubleshooting-errors) section. (If your problem isn't listed there, reach out to me on Twitter and I'll try to help debug!)

### Step 4: (Optional) Add a link to your RSS feed to your site

Now that you've done all this work to generate an RSS feed for your site, it's time to make it discoverable by your readers! (This step is technically optional, although it's probably a good idea if you want people to actually find your RSS feed.)

Use Gatsby's `<Link>` component to add a link to your RSS feed somewhere on your home page. (Not sure where to put it? The header or footer is a good start.)

```javascript
// import { Link } from 'gatsby'
<Link to="/rss.xml">RSS feed</Link>
```

If your prod build is still running from Step 3, you'll need to kill the server (with `ctrl + c` or by closing the terminal window) and rerun the build/serve command to see your changes. (Production builds don't have hot reloading, so you need to restart the server for your changes to be picked up.)

> You could also test the link using the development server (by running `npm run develop`), but since the RSS feed doesn't get generated for dev builds, you'll get a 404 when you actually click on the link.

And you're done! The next time you deploy your site, you should be able to visit `<your-domain>/rss.xml` (or whatever you configured `output` to be) and see your own RSS feed.

## Troubleshooting Errors

To help troubleshoot, here are some of the errors I ran into as I was implementing this on my own site, as well as the solution for how I fixed the problem.

{% details Cannot query field "siteUrl" on type "SiteSiteMetadata". %}

**The Error Message:**

```shell
     ERROR #11321  PLUGIN

    "gatsby-plugin-feed" threw an error while running the onPostBuild
    lifecycle:

    Cannot query field "siteUrl" on type "SiteSiteMetadata".

    GraphQL request:7:11
    6 |           description
    7 |           siteUrl
      |           ^
    8 |           site_url: siteUrl, Cannot query field "siteUrl" on type
     "SiteSiteMetadata".

    GraphQL request:8:11
    7 |           siteUrl
    8 |           site_url: siteUrl
      |           ^
    9 |         }
```

**The Underlying Problem:** Your site doesn't have site metadata, which the default query in `gatsby-plugin-feed` expects there to be.

**The Solution:** Add site metadata to your `gatsby-config.js` file. See [Prerequisite: Confirm that you have `siteMetadata`](#prerequisite-confirm-that-you-have-sitemetadata).

{% enddetails %}

{% details Cannot query field "fields" on type "MarkdownRemark". %}

**The Error Message:**

```
ERROR #11321  PLUGIN

"gatsby-plugin-feed" threw an error while running the onPostBuild
lifecycle:

Cannot query field "fields" on type "MarkdownRemark".

GraphQL request:16:15
15 |               }
16 |               fields {
   |               ^
17 |                 slug
```

**The Underlying Problem:** Your blog posts don't have a `slug` field. The default GraphQL query used by `gatsby-plugin-feed` expects your MarkdownRemark nodes to have a `fields.slug` property. (You can use the GraphiQL explorer to manually inspect the available fields on the MarkdownRemark query and confirm that `fields.slug` is not available.)

**The Solution:** In your configuration for `gatsby-plugin-feed` (in the `gatsby-config.js` file), make sure the `feed.query` option includes a `slug` field in the query. See the section on [Configuring `options.query` and `feed.query`](#configuring-optionsquery-and-feedquery) in Step 2.

{% enddetails %}

## Takeaways

* Adding an RSS feed to your site lets readers get automatic updates when you post new content.
* To add an RSS feed to a Gatsby site, use the `gatsby-plugin-feed` plugin. It only generates RSS feeds on production-style builds.

## Wrap It Up

Now that you've added an RSS feed to this sample blog site, try adding one to your own Gatsby site. If you run into problems, you can reach out to me on Twitter and I'll do my best to help troubleshoot.

Want to be notified when I write new content? You can subscribe to the [RSS feed for my own blog](https://meganesulli.com/rss.xml), or you can [follow me on Twitter](https://twitter.com/meganesulli).

## Additional Resources

- [Gatsby How-To Guide: Adding an RSS Feed](https://www.gatsbyjs.com/docs/how-to/adding-common-features/adding-an-rss-feed/)
- [`gatsby-plugin-feed` README](https://www.gatsbyjs.com/plugins/gatsby-plugin-feed/)
- [How do RSS Feeds Work](https://rss.com/blog/how-do-rss-feeds-work/)
- [What is RSS](https://rss.softwaregarden.com/aboutrss.html): Intro to RSS feeds and RSS aggregators
- [RSS Specification](https://www.rssboard.org/rss-specification)