{"post":{"slug":"how-to-build-a-rss-feed-2026","title":"How to build a RSS feed in 2026","excerpt":"RSS feeds are having a quiet resurgence in 2026 — newsletters, podcasts, and AI aggregators all consume them. This guide covers building a valid RSS 2.0 and Atom feed for any web framework.","category":"code","date":"April 14, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">R</span>SS 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.\n\n## What changed in 2026\n\n- **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.\n- **JSON Feed 1.1** gained adoption as a cleaner alternative to RSS XML; it is easier to parse and increasingly supported by major readers.\n- **Podcast feeds** are still RSS 2.0 with Apple/Spotify namespace extensions — the format is not going anywhere.\n- **Astro and Nuxt** ship RSS feed helpers in their standard libraries, making static generation trivial.\n\n## Format comparison\n\n| Format | Spec age | Parsing | Reader support | Best for |\n| --- | --- | --- | --- | --- |\n| RSS 2.0 | 2002 | XML | Universal | Blogs, podcasts |\n| Atom 1.0 | 2005 | XML | Universal | Blogs (stricter spec) |\n| JSON Feed 1.1 | 2017 | JSON | Growing | Developer-focused blogs |\n\n## Using the `feed` package (any framework)\n\n```bash\nnpm install feed\n```\n\n```ts\n// lib/feed.ts\nimport { Feed } from 'feed'\n\nexport function buildFeed(posts: Post[]) {\n  const feed = new Feed({\n    title: 'ByteLedger Blog',\n    description: 'Software engineering and finance for developers',\n    id: 'https://byteledger.dev/',\n    link: 'https://byteledger.dev/',\n    language: 'en',\n    copyright: `All rights reserved ${new Date().getFullYear()}, ByteLedger`,\n    feedLinks: {\n      rss2: 'https://byteledger.dev/feed.xml',\n      atom: 'https://byteledger.dev/atom.xml',\n      json: 'https://byteledger.dev/feed.json',\n    },\n    author: { name: 'ByteLedger Team', email: 'hello@byteledger.dev' },\n  })\n\n  for (const post of posts) {\n    feed.addItem({\n      title:       post.title,\n      id:          `https://byteledger.dev/blog/${post.slug}`,\n      link:        `https://byteledger.dev/blog/${post.slug}`,\n      description: post.excerpt,\n      date:        new Date(post.date),\n      category:    [{ name: post.category }],\n    })\n  }\n\n  return feed\n}\n```\n\n## Next.js App Router route\n\n```ts\n// app/feed.xml/route.ts\nimport { buildFeed } from '@/lib/feed'\nimport { getAllPosts } from '@/lib/posts'\n\nexport async function GET() {\n  const posts = await getAllPosts()\n  const feed = buildFeed(posts)\n\n  return new Response(feed.rss2(), {\n    headers: { 'Content-Type': 'application/rss+xml; charset=utf-8' },\n  })\n}\n```\n\nFor Atom: `feed.atom1()`. For JSON Feed: `feed.json1()` with Content-Type `application/feed+json`.\n\n## Astro\n\n```ts\n// src/pages/feed.xml.ts\nimport rss from '@astrojs/rss'\nimport { getCollection } from 'astro:content'\n\nexport async function GET(context: APIContext) {\n  const posts = await getCollection('posts')\n\n  return rss({\n    title: 'ByteLedger Blog',\n    description: 'Software engineering and finance for developers',\n    site: context.site!,\n    items: posts.map(p => ({\n      title:       p.data.title,\n      pubDate:     new Date(p.data.date),\n      description: p.data.excerpt,\n      link:        `/blog/${p.slug}/`,\n    })),\n  })\n}\n```\n\n## Add autodiscovery to your HTML `<head>`\n\n```html\n<link\n  rel=\"alternate\"\n  type=\"application/rss+xml\"\n  title=\"ByteLedger Blog\"\n  href=\"/feed.xml\"\n/>\n<link\n  rel=\"alternate\"\n  type=\"application/atom+xml\"\n  title=\"ByteLedger Blog (Atom)\"\n  href=\"/atom.xml\"\n/>\n```\n\nWithout this, users and RSS readers can't auto-discover the feed from your homepage URL.\n\n## Validate your feed\n\n| Validator | URL |\n| --- | --- |\n| W3C Feed Validator | validator.w3.org/feed |\n| Feed Validator (feedvalidator.org) | feedvalidator.org |\n| JSON Feed validator | jsonfeed.org/validator |\n\nAlways run your feed through a validator before publishing — common errors include wrong date formats, missing required fields, and entities that need escaping.\n\n## Common mistakes\n\n**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.\n\n**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.\n\n**Serving HTML content without CDATA** — if your `<description>` contains HTML, wrap it in `<![CDATA[ ... ]]>` to avoid XML parsing errors.\n\n**Forgetting the autodiscovery link** — the feed URL is useless if readers can't find it from your homepage.\n\n## What to skip\n\n- **Building your own XML string with template literals** — character escaping and date formatting are easy to get wrong. Use a library.\n- **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.\n- **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.\n\n## FAQ\n\n**Should I serve RSS or Atom?**\nServe 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.\n\n**How often should the feed update?**\nThe 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).\n\n**Can I add analytics to RSS?**\nYou 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.\n\n**What is JSON Feed and should I add it?**\nJSON 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.\n\n## Where to go next\n\n- [How to add search to a site in 2026](/blog/how-to-add-search-to-a-site-2026)\n- [How to parse a PDF in 2026](/blog/how-to-parse-a-pdf-2026)\n- [How to generate a PDF in 2026](/blog/how-to-generate-a-pdf-2026)","hue":322,"metaTitle":"How to Build a RSS Feed in 2026 (Any Framework)","metaDescription":"Step-by-step guide to building an RSS feed in 2026. RSS 2.0, Atom, JSON Feed, Next.js, Astro, and plain Node — with validation and autodiscovery tips.","keywords":["how to build a rss feed 2026","rss feed tutorial 2026","rss feed next.js 2026","rss feed astro 2026","atom feed vs rss 2026","json feed 2026","rss feed node.js","generate rss feed javascript","rss autodiscovery","blog rss feed 2026"],"tldr":["**RSS 2.0 is the widest-supported format** — use it unless you have a specific reason to choose Atom or JSON Feed.","**Generate at build time for static sites** — no server needed; regenerate the feed on every deployment.","**`<lastBuildDate>` and `<pubDate>`** must be RFC 822 dates, not ISO strings — many validators reject the wrong format.","**Autodiscovery `<link>` tag** in your HTML `<head>` lets browsers and RSS readers find the feed without manual entry.","**Skip:** building your own XML serialiser — the `feed` npm package handles RSS 2.0, Atom, and JSON Feed with a type-safe API."],"readTime":"4 min read","wordCount":893},"related":[{"slug":"how-to-generate-a-pdf-2026","title":"How to generate a PDF in 2026","excerpt":"PDF generation covers invoices, reports, and certificates — each with different tooling tradeoffs. This guide picks the right library for your use case and walks through working examples.","category":"code","date":"March 12, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":306},{"slug":"how-to-build-a-website-2026","title":"How to build a website in 2026: from zero to live in a weekend","excerpt":"Building a website in 2026 is faster than ever, but the options are more confusing. This guide picks the right path for your goal and skill level.","category":"code","date":"March 28, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":306},{"slug":"how-to-make-an-api-2026","title":"How to Make an API in 2026: a beginner walkthrough","excerpt":"A plain-language guide to building your first API in 2026: what an endpoint is, how to design routes, return JSON, and ship it, with a small working example.","category":"code","date":"June 15, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":212}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"how-to-build-a-rest-api-2026","title":"How to build a REST API in 2026: the complete guide","excerpt":"REST APIs are still the backbone of most web backends in 2026. This guide covers design, implementation, validation, auth, and deployment correctly.","category":"code","date":"April 14, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":322},"older":{"slug":"how-to-build-sinking-funds-2026","title":"How to build sinking funds in 2026: save for every irregular expense","excerpt":"Sinking funds turn surprise bills into planned expenses. Here is how to set up, size, and automate them in 2026 so nothing ever blindsides your budget.","category":"finance","date":"April 14, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":284}}