RSS feeds were briefly declared dead, then quietly came back. In 2026 they're consumed by newsletters (Substack, Beehiiv), podcast apps, AI content aggregators, and a resurgent group of readers who prefer chronological feeds over algorithmic timelines. Adding one to your site takes under an hour and improves discoverability with zero ongoing maintenance.
What changed in 2026
- AI content aggregators (Perplexity, Feedly AI, various LLM-powered readers) now subscribe to RSS to discover and index web content — feeds matter for AI visibility, not just human readers.
- JSON Feed 1.1 gained adoption as a cleaner alternative to RSS XML; it is easier to parse and increasingly supported by major readers.
- Podcast feeds are still RSS 2.0 with Apple/Spotify namespace extensions — the format is not going anywhere.
- Astro and Nuxt ship RSS feed helpers in their standard libraries, making static generation trivial.
Format comparison
| Format |
Spec age |
Parsing |
Reader support |
Best for |
| RSS 2.0 |
2002 |
XML |
Universal |
Blogs, podcasts |
| Atom 1.0 |
2005 |
XML |
Universal |
Blogs (stricter spec) |
| JSON Feed 1.1 |
2017 |
JSON |
Growing |
Developer-focused blogs |
Using the feed package (any framework)
npm install feed
// lib/feed.ts
import { Feed } from 'feed'
export function buildFeed(posts: Post[]) {
const feed = new Feed({
title: 'ByteLedger Blog',
description: 'Software engineering and finance for developers',
id: 'https://byteledger.dev/',
link: 'https://byteledger.dev/',
language: 'en',
copyright: `All rights reserved ${new Date().getFullYear()}, ByteLedger`,
feedLinks: {
rss2: 'https://byteledger.dev/feed.xml',
atom: 'https://byteledger.dev/atom.xml',
json: 'https://byteledger.dev/feed.json',
},
author: { name: 'ByteLedger Team', email: 'hello@byteledger.dev' },
})
for (const post of posts) {
feed.addItem({
title: post.title,
id: `https://byteledger.dev/blog/${post.slug}`,
link: `https://byteledger.dev/blog/${post.slug}`,
description: post.excerpt,
date: new Date(post.date),
category: [{ name: post.category }],
})
}
return feed
}
Next.js App Router route
// app/feed.xml/route.ts
import { buildFeed } from '@/lib/feed'
import { getAllPosts } from '@/lib/posts'
export async function GET() {
const posts = await getAllPosts()
const feed = buildFeed(posts)
return new Response(feed.rss2(), {
headers: { 'Content-Type': 'application/rss+xml; charset=utf-8' },
})
}
For Atom: feed.atom1(). For JSON Feed: feed.json1() with Content-Type application/feed+json.
Astro
// src/pages/feed.xml.ts
import rss from '@astrojs/rss'
import { getCollection } from 'astro:content'
export async function GET(context: APIContext) {
const posts = await getCollection('posts')
return rss({
title: 'ByteLedger Blog',
description: 'Software engineering and finance for developers',
site: context.site!,
items: posts.map(p => ({
title: p.data.title,
pubDate: new Date(p.data.date),
description: p.data.excerpt,
link: `/blog/${p.slug}/`,
})),
})
}
Add autodiscovery to your HTML <head>
<link
rel="alternate"
type="application/rss+xml"
title="ByteLedger Blog"
href="/feed.xml"
/>
<link
rel="alternate"
type="application/atom+xml"
title="ByteLedger Blog (Atom)"
href="/atom.xml"
/>
Without this, users and RSS readers can't auto-discover the feed from your homepage URL.
Validate your feed
| Validator |
URL |
| W3C Feed Validator |
validator.w3.org/feed |
| Feed Validator (feedvalidator.org) |
feedvalidator.org |
| JSON Feed validator |
jsonfeed.org/validator |
Always run your feed through a validator before publishing — common errors include wrong date formats, missing required fields, and entities that need escaping.
Common mistakes
ISO 8601 dates instead of RFC 822 — RSS 2.0 requires dates like Mon, 02 Jun 2026 00:00:00 +0000, not 2026-06-02T00:00:00Z. The feed package handles this automatically; manual XML generation does not.
No <guid> or duplicate guids — every item needs a unique, permanent <guid>. Use the full URL as the guid if you don't have a separate ID.
Serving HTML content without CDATA — if your <description> contains HTML, wrap it in <![CDATA[ ... ]]> to avoid XML parsing errors.
Forgetting the autodiscovery link — the feed URL is useless if readers can't find it from your homepage.
What to skip
- Building your own XML string with template literals — character escaping and date formatting are easy to get wrong. Use a library.
- Full post HTML in the feed — many readers render it, but it bloats the feed significantly. A summary + link is the standard pattern; full content is opt-in.
- Rate-limiting the feed endpoint too aggressively — RSS readers poll on a schedule; a 5-second cache is fine but blocking legitimate crawlers hurts you.
FAQ
Should I serve RSS or Atom?
Serve both — they take 2 extra lines of code and every reader supports both. Default the <link> autodiscovery tag to RSS 2.0 since it has broader recognition.
How often should the feed update?
The feed should update whenever new posts are published. For build-time generation, every deployment regenerates it. For server-rendered routes, add a short cache (60–300 seconds).
Can I add analytics to RSS?
You can add UTM parameters to item links (?utm_source=rss) to track clicks in GA/Posthog. You cannot easily track how many readers subscribed without a third-party service like Feedburner (deprecated) or Feedly's subscriber count API.
What is JSON Feed and should I add it?
JSON Feed 1.1 is an RSS alternative using JSON instead of XML. It's easier to parse programmatically and growing in reader support. If you're already using the feed package, adding feed.json1() costs one extra route.
Where to go next