Astro Blog RSS Feed
Tags: jamstack, astro, react, tutorial
Published: 2023-05-16 08:14:36

---
title: "Astro Blog RSS Feed"
series: "Astro Beginners’ Tutorial"
published: "true"
description: "Astro Blog RSS Feed: use Astro Collections to add an RSS feed to your blog site, for visitors who prefer RSS."
tags: "jamstack, astro, react, tutorial"
canonical_url: "https://cloudcannon.com/tutorials/astro-beginners-tutorial-series/astro-blog-rss-feed/"
---

**In this lesson you will learn how to add a blog post RSS feed in Astro.**

## Astro Blog Post RSS Feed

So far in this [Astro Beginners&rsquo; tutorial](https://cloudcannon.com/tutorials/astro-beginners-tutorial-series/), all the routes on the site have been HTML pages; whether generated from Astro markup files or Markdown.  In this final lesson, we see Astro can also generate non-HTML content.  We focus on adding an Astro Blog RSS feed, though you can use can follow a similar approach to serve a PDF brochure or even some JSON data from your Astro site.

## Astro Resource Routes

We saw earlier, if we want Astro to generate an HTML page, we create a `.astro`, Markdown or HTML file within the `src/pages` directory. Astro generates the page using that input file&rsquo;s path to determine the route the content is served from. So `https://example.com/docs/getting-started` might be generated from `src/pages/docs/getting-started.astro`.

For non-HTML routes, we follow the same file-based routing pattern, still creating the source file within `src/pages`.  One difference, though, is that for non-HTML routes, you need to create a TypeScript (or JavaScript) file (instead of `.astro`, etc.).  Going back to the PDF brochure example, you might create `src/pages/brochure.pdf.ts` to make the brochure available at the endpoint `https://example.com/brochure.pdf`.

### Astro non-HTML Route Source

Continuing the PDF example, within the endpoint file we need to export a handler function.  That handler function will perform two tasks:

1. listen for HTTP `GET` requests (sent by a site visitor&rsquo;s browser requesting the PDF, for example)
2. return an HTTP response with the requested data

Here is what the code might look like for the PDF endpoint:

```typescript
// EXAMPLE ONLY
import type { APIRoute } from 'astro';

export const get: APIRoute = async function get(context) {

  const doc = /* logic to create a PDF file */;

  return { body: doc.toString('binary'), encoding: 'binary' };
}
```

It is important that the exported handler is named `get` (to match the request HTTP method). The `context` parameter on the `get` function makes two objects available:

- `context.params`, which you only have access to  if you create dynamic routes here, like we had for blog posts.
- `context.request` is an object with an `url` field: the URL the incoming request was sent to.

The response will always have a `body` field. Here, the response will be binary data (rather than text), so we include the optional `encoding` field.  You can omit `encoding` if sending a JSON text response, for example.

## Astro Blog RSS Feed Endpoint

RSS feeds are typically sent as plain text in XML format.  We could manually construct a text string containing the XML content, then use that as the response body. However, the `@astrojs/rss` package makes things easier for us.

Run the command below to install the package:

```shell
pnpm add @astrojs/rss
```

Now we can add some source code for the XML RSS feed route.  Create `src/pages/rss.xml.ts` with the following content:

```typescript
import rss from "@astrojs/rss";
import type { APIRoute } from "astro";
import { getCollection } from "astro:content";

export const get: APIRoute = async function get({ site }) {
  const posts = await getCollection("blog");
  const items = [];

  return rss({
    title: "Novelty Cycles",
    description: "Novelty Cycles Blog Posts",
    site: site.toString(),
    customData: `<language>en</language>`,
    items
  });
}
```

The XML site map will include some site metadata, as well as the list of blog posts.  For the moment, we just have an empty array for the posts.  In the `get` function signature, we are destructuring `site` from `context`.   `site` is a global setting within an Astro project; it is the final site&rsquo;s URL.  We can set the value in `astro.config.mjs`.

Update `astro.config.mjs` with a site field.  Use `https://example.com` if you do not yet have a value for your own site:

```javascript
import { defineConfig } from "astro/config";

import react from "@astrojs/react";

export default defineConfig({
  site: "https://example.com",
  integrations: [react()],
});
```

Go to `http://localhost:3000/rss.xml` in your browser, and you should see something like this displayed.  The content should match, though the styling might vary depending on which browser you are using:

![Astro Blog Post RSS Feed: Screen capture shows raw XML code in the browser for the site. Only site meta is visible, including site title, description, link and language.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ia71pc1f2lrb046ab1o8.png)

You can see the `astrojs/rss` package generated the response body for you, using the data you provided.  In the snippet above, language is set to `en`.  You can change this to match your site&rsquo;s language.  The XML format is exactly what RSS readers need.  The content is designed for those readers, rather than for humans to read. That said, XML supports styling, and you can add a stylesheet.

Let&rsquo;s add the missing `items` field.  `@astrojs/rss` just expects an array of blog posts.  Each item will need the following fields:

```typesscript
- title
- description
- link
- pubDate (as a Date object)
```

If you are already feeling confident with your Astro Content Collection skills, have a go at adding in the items array yourself, before reading on.

### Items Array

Just like when we created the blog roll on the home page, we want the posts to be in reverse chronological order (newest first).  We&rsquo;ll use `Array.sort` once more:

```javascript
  const items = posts
    .sort((a, b) => Date.parse(b.data.date) - Date.parse(a.data.date));
```

That puts the posts in the right order, and to finish, we just have to map the fields output by `getCollection` to the ones we need for `items`.  Here is the complete handler function:

```typescript
export const get: APIRoute = async function get({ site }) {
  const posts = await getCollection("blog");

  const items = posts
    .sort((a, b) => Date.parse(b.data.date) - Date.parse(a.data.date))
    .map(({ data: { date, title, description }, slug }) => ({
      title,
      description,
      link: `${site}${slug}`,
      pubDate: new Date(date),
    }));

  return rss({
    title: "Novelty Cycles",
    description: "Novelty Cycles Blog Posts",
    site: site.toString(),
    customData: "<language>en</language>",
    items,
  });
};
```

The final RSS feed (at `http://localhost:3000/rss.xml`) should look something like this:

![Astro Blog Post RSS Feed: Screen capture shows raw XML code in the browser for the site, with an entry for each blog post.  Blog post entries include a title, link, description and pub Date.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8t9ryv13bdoq7bmyg1qe.png)

Once you have built the site using your Content Management System (**CMS**), you will be able to open up the feed in an RSS reader.

![Astro Blog R S S Feed: R S S reader screen capture shoes the three blog posts listed with the Unicycle post selected.  Reading time, title ans description are shown for this selected post.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/93xiii2ax9irldr6lgrr.png)

## RSS Link Tag

Typically you will add an RSS feed link to site headers or footers.  You can also add a meta tag to help RSS readers find the feed.  It is fine to put this tag on every site page, using `src/layout/BaseLayout.astro`:

```html
<html lang="en">
  <head>
    <!-- ...TRUNCATED -->
    <link
      rel="alternate"
      title="Novelty Cycles Blog Posts"
      href="/rss.xml"
      type="application/rss+xml"
    />
  </head>
  <!-- TRUNCATED... -->
```

## Congratulations!

Well done on making it to the end and completing the tutorial.  Hopefully, it has provided enough to get you going on your next Astro project.  Excited to hear what you will do with Astro.  Visit the Astro docs to learn about more advanced Astro features.

![Astro Cloud Cannon Visual Editor: Screen capture shows a Markdown source file being edited in-browser in the Cloud Cannon Visual Editor.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ly24caguzwiz2luwjqzp.png)

![Astro Cloud Cannon Content Editor: Screen capture shows a blog post being edited in-browser.  Available fields include a Title, Description, Date and Draft check box, as well as a large field for blog post body content.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ns65qe69z0maf7b1e4h5.png)

If you are building a content site, [give CloudCannon a try](https://cloudcannon.com/astro-cms/).  It&rsquo;s a CMS that lets the entire team focus on what they do best. Developers build the site using their preferred code editor and commit source code to a Git repo, as usual.  Content creators can choose between seeing their edits live, onscreen, with the visual editor, or alternatively, writing long-form WYSIWYG content in the content editor. When they save their work, CloudCannon syncs changes back to the Git repo and automatically rebuilds your site. The whole team uses the tools they are most comfortable with.

I wish you the best of luck on your Astro journey and trying out CloudCannon &mdash; the stars are the limit!