I was setting up a new reader app — one of the AI-aware ones that summarizes feeds and lets you query across them — and noticed it pulled my JSON Feed an order of magnitude faster than my RSS. Not because JSON is inherently faster over the wire (it's not, the payloads were similar sizes), but because the reader was built in a JavaScript stack and consuming JSON was a single fetch and JSON.parse, while RSS required a dedicated XML parser.
That reader is not unusual. The newer aggregators, AI feed-summarizers, and reader apps are JS-native. Many of them accept JSON Feed as a first-class input and treat RSS as legacy. Shipping both is how you cover the full audience without choosing sides.
What JSON Feed Is
JSON Feed is a syndication format defined at jsonfeed.org in 2017 by Brent Simmons and Manton Reece. The spec is intentionally short and the format is intentionally boring: it is what you would design if you were translating Atom or RSS 2.0 into idiomatic JSON and trying very hard not to invent new concepts along the way.
A minimal feed looks like this:
{
"version": "https://jsonfeed.org/version/1.1",
"title": "My Blog",
"home_page_url": "https://example.com/",
"feed_url": "https://example.com/feed.json",
"description": "Notes on shipping static sites.",
"authors": [
{ "name": "Jane Smith", "url": "https://example.com/about" }
],
"language": "en-US",
"items": [
{
"id": "https://example.com/posts/first-post",
"url": "https://example.com/posts/first-post",
"title": "First Post",
"content_html": "<p>Hello, world.</p>",
"date_published": "2026-04-18T10:00:00-06:00",
"date_modified": "2026-04-18T10:00:00-06:00",
"tags": ["intro", "meta"]
}
]
}
Every field maps almost one-to-one to an RSS or Atom equivalent. The version field is URL-based on purpose: fetching it returns the spec.
Why Newer Aggregators Prefer It
Three reasons, in order of importance.
First, parsing. Every modern programming language has JSON in the standard library. RSS and Atom both require an XML parser, which is either a heavy dependency or a fragile hand-rolled mess. A JS-based reader app consuming JSON Feed is 5 lines of code. Consuming RSS is a library import and a defensive wrapper around the library's handling of malformed feeds.
Second, extensibility. JSON Feed has a designed extension mechanism: any key starting with an underscore is reserved for custom extensions. If a reader wants to add a namespace, it does not need a separate XML namespace declaration and a new parser path. Just add _reader_name: { ... } and move on. RSS has extensions too, but they are XML namespaces and they are a pain to parse.
Third, AI aggregators care about structure. When a feed-consuming AI summarizer parses your posts, the HTML inside content_html is what it extracts for summarization. JSON's delimiter structure is unambiguous — there is no CDATA ambiguity, no escape-vs-entity question, no "did the feed publisher forget to wrap in CDATA" parsing failure. The AI gets clean content out and can summarize accurately.
Why Keep RSS Too
RSS is not going anywhere. Feedly, NetNewsWire, Inoreader, podcast apps, email-to-RSS bridges, mastodon-style feed-import tools — all of these speak RSS or Atom as their primary. Dropping RSS because you shipped JSON Feed would cut off a huge chunk of your actual readers.
The right answer is both. One post source, two output formats. Neither is hard to generate.
Generating Both from the Same Source in Eleventy
The pattern I use: one data structure, two templates. In Eleventy, a posts collection is built from your Markdown files. The RSS template emits XML, the JSON Feed template emits JSON, both iterate the same collection.
The RSS template:
---
permalink: /feed.xml
eleventyExcludeFromCollections: true
---
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>J.A. Watte</title>
<link>https://jwatte.com/</link>
<atom:link href="https://jwatte.com/feed.xml" rel="self" type="application/rss+xml" />
<description>Author of The W-2 Trap, The Condo Trap, The $97 Launch, The $20 Dollar Agency, The Resale Trap, and The $100 Network. Why doing everything right financially still doesn't work — and what to do about it.</description>
<language>en-us</language>
</channel>
</rss>
The JSON Feed template, iterating the same collection:
---
permalink: /feed.json
eleventyExcludeFromCollections: true
---
{
"version": "https://jsonfeed.org/version/1.1",
"title": "J.A. Watte",
"home_page_url": "https://jwatte.com",
"feed_url": "https://jwatte.com/feed.json",
"description": "Author of The W-2 Trap, The Condo Trap, The $97 Launch, The $20 Dollar Agency, The Resale Trap, and The $100 Network. Why doing everything right financially still doesn't work — and what to do about it.",
"language": "en-US",
"items": [
]
}
The | dump Nunjucks filter is what turns strings into JSON-safe quoted values. It handles the escape cases RSS makes you think about.
Discovery: The <link> Tags
Both feeds should be discoverable from the site's <head>:
<link rel="alternate" type="application/rss+xml" title="RSS Feed" href="/feed.xml">
<link rel="alternate" type="application/feed+json" title="JSON Feed" href="/feed.json">
The type attribute is different for each, and the application/feed+json MIME type is the one the JSON Feed spec specifies. Some older tools may accept application/json, but the spec-compliant type is the one to use.
Content-Type Headers
For the served files, the Netlify config:
[[headers]]
for = "/feed.xml"
[headers.values]
Content-Type = "application/rss+xml; charset=utf-8"
Cache-Control = "public, max-age=1800"
[[headers]]
for = "/feed.json"
[headers.values]
Content-Type = "application/feed+json; charset=utf-8"
Cache-Control = "public, max-age=1800"
30-minute cache is about right for a blog that publishes daily or less. Busier sites may want 5-10 minute caches.
The Spec Features Worth Using
A few JSON Feed fields that do not have clean RSS equivalents and are worth adding:
banner_imageper-item: the hero image for the post. JSON Feed makes this a first-class field. RSS requires awkward enclosure or media namespace tricks.summary: a short description separate from the full content. Useful for readers that show a preview list.external_url: if an item is a link-blog post pointing to somewhere else, this field holds the external URL whileurlstays on your site._*custom extensions: for site-specific metadata a specific reader cares about.
What About Atom?
Atom (application/atom+xml) is the pedantic, well-specified sibling of RSS. Some reader apps prefer it. If you want to cover every base, emit Atom as a third feed at /feed.atom. For most sites, RSS and JSON Feed are enough — the Atom audience is almost fully covered by RSS support.
Where the Analyzer Checks
The audit at /tools/mega-analyzer/ detects both feeds from <link rel="alternate"> tags and validates the served content. JSON Feed is parsed against the v1.1 spec. RSS is parsed against the 2.0 schema. Either one missing is flagged; both missing is a higher-severity flag.
The Short Version
- JSON Feed at
/feed.jsonis the modern sibling of RSS at/feed.xml. - Newer aggregators, AI feed-summarizers, and JS-native readers prefer JSON Feed.
- RSS still has the largest installed base. Keep it.
- One source, two templates. Eleventy (or any static generator) can emit both from the same post collection.
- Link both feeds from
<head>withrel="alternate", serve them with correct Content-Type headers. application/feed+jsonis the correct MIME for JSON Feed.