Skip to content

Adding RSS Feeds to Your Astro Blog

Hermes Team · · 2 min read
RSS feed icon

RSS feeds let readers subscribe to your blog and get notified of new posts. They’re essential for content sites—and Astro makes them trivial to add.

Why RSS Still Matters

  • Newsletter tools — Many use RSS as the source for email digests
  • Aggregators — Feedly, Inoreader, and others rely on RSS
  • Podcast apps — RSS powers podcast distribution
  • Developer audience — Many developers prefer RSS over social algorithms

Installing the RSS Package

npm install @astrojs/rss

Creating the Feed

Add a new page at src/pages/rss.xml.js (or .ts):

import rss from '@astrojs/rss';
import { getCollection } from 'astro:content';

export async function GET(context) {
  const posts = (await getCollection('blog'))
    .filter((p) => !p.data.draft)
    .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());

  return rss({
    title: 'My Blog',
    description: 'Latest posts from my blog',
    site: context.site,
    items: posts.map((post) => ({
      title: post.data.title,
      description: post.data.description ?? post.data.title,
      pubDate: post.data.pubDate,
      link: `/blog/${post.slug}/`,
    })),
    customData: `<language>en-us</language>`,
  });
}

Visit /rss.xml and you’ll have a valid RSS 2.0 feed.

JSON Feed

Some readers prefer JSON Feed. Create src/pages/feed.json.js:

import rss from '@astrojs/rss';
import { getCollection } from 'astro:content';

export async function GET(context) {
  const posts = (await getCollection('blog'))
    .filter((p) => !p.data.draft)
    .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());

  return rss({
    title: 'My Blog',
    description: 'Latest posts',
    site: context.site,
    items: posts.map((post) => ({
      title: post.data.title,
      description: post.data.description ?? post.data.title,
      pubDate: post.data.pubDate,
      link: `/blog/${post.slug}/`,
    })),
    stylesheet: false,
    format: 'json',
  });
}

Linking the Feed

Add auto-discovery in your layout’s <head>:

<link
  rel="alternate"
  type="application/rss+xml"
  title="My Blog RSS"
  href="/rss.xml"
/>

For JSON Feed:

<link
  rel="alternate"
  type="application/json"
  title="My Blog JSON Feed"
  href="/feed.json"
/>

Including Full Content

By default, items use description (excerpt). For full post body:

import { render } from './render.mjs'; // or use a markdown renderer

items: await Promise.all(
  posts.map(async (post) => ({
    title: post.data.title,
    pubDate: post.data.pubDate,
    link: `/blog/${post.slug}/`,
    content: (await render(post)).html,
  }))
),

Full-content feeds are larger but some readers prefer them for offline reading.

Limiting Items

Most feeds include the last 10–20 posts. Slice the array:

const recent = posts.slice(0, 20);

Caching

Static builds generate the feed at build time. No runtime cost. For SSG, the feed is always fresh when you deploy.


RSS is one of those features that takes 10 minutes to add and pays off for years. Your readers will thank you.