Feedsmith: The Swiss Army Knife of Feed Parsing and Generation
Every web developer who has worked with syndication feeds knows the pain. RSS has multiple versions, Atom has its own quirks, JSON Feed showed up fashionably late, and RDF still lurks in the shadows. Most libraries force you to pick a side: parse or generate, one format or another. Feedsmith refuses to compromise. It parses and generates RSS, Atom, RDF, and JSON Feed formats in a single package, and it does so faster than just about anything else in the JavaScript ecosystem.
Whether you are building a podcast aggregator, an RSS reader, or a backend service that needs to both consume and produce feeds, feedsmith has you covered with comprehensive format support and over 30 recognized namespaces.
Why Feedsmith Stands Out
Most feed libraries specialize. rss-parser only parses. The feed package only generates. Feedsmith does both, and it does both well. Here is what makes it worth your attention:
- Universal parsing that auto-detects RSS, Atom, RDF, and JSON Feed formats
- Feed generation for RSS, Atom, and JSON Feed
- OPML support for reading and working with outline files
- 30+ namespace support including Dublin Core, iTunes, Media RSS, Podcast Index, YouTube, Spotify, and more
- TypeScript-first with complete type definitions for every format
- Lenient processing that gracefully handles malformed real-world feeds
- Tree-shakable so you only bundle what you use
- 2000+ tests with 99% code coverage
Getting It Into Your Project
Install feedsmith with your package manager of choice:
// npm
// npm install feedsmith
// yarn
// yarn add feedsmith
// pnpm
// pnpm add feedsmith
The package ships both CommonJS and ESM builds, supports Node.js 14+ and modern browsers, and can even be loaded from a CDN for quick prototyping.
Your First Feed, Parsed in Seconds
The Universal Approach
The simplest way to parse any feed is with parseFeed. It examines the content, figures out the format, and returns a structured result:
import { parseFeed } from 'feedsmith'
const response = await fetch('https://example.com/feed.xml')
const content = await response.text()
const { format, feed } = parseFeed(content)
console.log(`Detected format: ${format}`)
console.log(`Feed title: ${feed.title}`)
console.log(`Number of items: ${feed.items?.length}`)
The returned format string tells you whether you got an RSS, Atom, RDF, or JSON feed, and the feed object preserves the original structure of that format rather than flattening everything into a generic shape. This matters when you need access to format-specific fields or namespace data.
Going Format-Specific
When you know exactly what kind of feed you are dealing with, use the dedicated parsers for tighter types and slightly less overhead:
import { parseRssFeed, parseAtomFeed, parseJsonFeed } from 'feedsmith'
const rssFeed = parseRssFeed(rssXml)
console.log(rssFeed.channel?.title)
console.log(rssFeed.channel?.items?.[0]?.title)
const atomFeed = parseAtomFeed(atomXml)
console.log(atomFeed.title)
console.log(atomFeed.entries?.[0]?.title)
const jsonFeed = parseJsonFeed(jsonString)
console.log(jsonFeed.title)
console.log(jsonFeed.items?.[0]?.title)
Each parser returns an object shaped exactly like the corresponding specification, so RSS gives you channel and items, Atom gives you entries, and JSON Feed follows the JSON Feed spec structure.
Reading OPML Files
OPML files are commonly used to export and import feed subscription lists. Feedsmith handles those too:
import { parseOpml } from 'feedsmith'
const opml = parseOpml(opmlContent)
console.log(`Subscription list: ${opml.head?.title}`)
for (const outline of opml.body?.outlines ?? []) {
console.log(`${outline.text}: ${outline.xmlUrl}`)
}
Forging Feeds From Scratch
Generating an RSS Feed
One of Feedsmith's standout features is feed generation. Creating a valid RSS feed takes just a few lines:
import { generateRssFeed } from 'feedsmith'
const rss = generateRssFeed({
title: 'The Developer Dispatch',
link: 'https://example.com',
description: 'Weekly insights from the trenches of web development',
language: 'en',
lastBuildDate: new Date(),
items: [
{
title: 'Why TypeScript Won',
link: 'https://example.com/posts/typescript-won',
description: 'A look at how TypeScript conquered the JavaScript world.',
pubDate: new Date('2026-03-01'),
guid: 'https://example.com/posts/typescript-won',
},
{
title: 'Edge Computing in 2026',
link: 'https://example.com/posts/edge-computing',
description: 'The state of running code at the edge.',
pubDate: new Date('2026-02-25'),
guid: 'https://example.com/posts/edge-computing',
},
],
})
// rss is a valid XML string ready to serve
Building an Atom Feed
Atom feeds follow a similar pattern with format-appropriate fields:
import { generateAtomFeed } from 'feedsmith'
const atom = generateAtomFeed({
id: 'urn:uuid:blog-feed-001',
title: 'The Developer Dispatch',
updated: new Date(),
authors: [{ name: 'Jane Developer' }],
links: [{ href: 'https://example.com', rel: 'alternate' }],
entries: [
{
id: 'urn:uuid:post-001',
title: 'Why TypeScript Won',
updated: new Date('2026-03-01'),
content: {
type: 'html',
value: '<p>A deep dive into TypeScript dominance.</p>',
},
},
],
})
Crafting a JSON Feed
JSON Feed generation is just as straightforward:
import { generateJsonFeed } from 'feedsmith'
const json = generateJsonFeed({
version: 'https://jsonfeed.org/version/1.1',
title: 'The Developer Dispatch',
home_page_url: 'https://example.com',
feed_url: 'https://example.com/feed.json',
items: [
{
id: 'post-001',
title: 'Why TypeScript Won',
content_html: '<p>TypeScript took over.</p>',
date_published: '2026-03-01T00:00:00Z',
},
],
})
Taming Namespaces and Messy Feeds
Working with Podcast and Media Namespaces
Real-world feeds are packed with namespace extensions. Feedsmith recognizes over 30 namespaces and normalizes them into clean, typed objects:
import { parseRssFeed } from 'feedsmith'
const podcastFeed = parseRssFeed(podcastXml)
const episode = podcastFeed.channel?.items?.[0]
// iTunes namespace fields
console.log(episode?.itunes?.duration)
console.log(episode?.itunes?.explicit)
console.log(episode?.itunes?.image?.href)
// Media RSS namespace
console.log(episode?.media?.content?.[0]?.url)
console.log(episode?.media?.content?.[0]?.type)
// Podcast Index namespace
console.log(episode?.podcast?.transcript?.[0]?.url)
Feedsmith handles custom namespace prefixes gracefully. Even if a feed uses a non-standard prefix, the library maps it to the correct namespace based on the URI.
Handling the Real World
Feeds in the wild are messy. Fields appear in the wrong case, dates come in bizarre formats, and some feeds barely qualify as valid XML. Feedsmith takes a lenient approach:
import { parseFeed } from 'feedsmith'
// Feedsmith handles all of these gracefully:
// - Case-insensitive field matching
// - Non-standard namespace URIs
// - Missing required fields
// - Mixed RSS versions
const { feed } = parseFeed(messyFeedContent)
// You still get a clean, typed result
console.log(feed.title)
This tolerance for imperfection makes Feedsmith practical for production use where you cannot control the quality of incoming feeds.
Bringing Type Safety to Feeds
Feedsmith ships complete TypeScript definitions for every supported format:
import type { Rss, Atom, Json, Opml } from 'feedsmith/types'
function processRssFeed(feed: Rss.Feed): void {
const channel = feed.channel
if (channel?.items) {
for (const item of channel.items) {
console.log(item.title, item.pubDate)
}
}
}
function processAtomFeed(feed: Atom.Feed): void {
if (feed.entries) {
for (const entry of feed.entries) {
console.log(entry.title, entry.updated)
}
}
}
Having distinct types for each format means your editor catches mistakes before runtime, and you get full autocompletion for format-specific fields and namespaces.
A Note on Speed
Benchmarks show Feedsmith running roughly 3x faster than rss-parser on large RSS files, 2.5x to 5.9x faster on Atom feeds, and even outpacing feed parsers written in Go, Ruby, and Python. For applications processing hundreds or thousands of feeds, that performance gap translates directly into lower server costs and faster response times.
Wrapping Up
feedsmith fills a gap that has existed in the JavaScript ecosystem for a long time. Instead of cobbling together one library for parsing and another for generation, or wrestling with incomplete namespace support, you get a single package that handles everything. It is fast, well-tested, TypeScript-native, and forgiving enough to handle the messy reality of feeds in the wild. If your project touches syndication in any way, Feedsmith deserves a spot in your dependencies.