Discoverability · Next.js (App Router)
Should my site publish an RSS feed in Next.js (App Router)
The fix for Next.js (App Router)
13x detects your framework from the response and hands you this version rather than the generic one — below 50% confidence it hedges and gives you the generic one instead.
Serve it from a route handler so it regenerates with your content:
// app/rss.xml/route.ts
import { getPosts } from "@/lib/posts";
export async function GET() {
const posts = await getPosts();
const items = posts
.map(
(post) => ` <item>
<title>${escapeXml(post.title)}</title>
<link>https://example.com/blog/${post.slug}</link>
<guid isPermaLink="true">https://example.com/blog/${post.slug}</guid>
<pubDate>${new Date(post.date).toUTCString()}</pubDate>
<description>${escapeXml(post.summary)}</description>
</item>`
)
.join("\n");
const body = `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>example.com</title>
<link>https://example.com</link>
<description>What you publish here</description>
<atom:link href="https://example.com/rss.xml" rel="self" type="application/rss+xml" />
${items}
</channel>
</rss>`;
return new Response(body, {
headers: { "content-type": "application/rss+xml; charset=utf-8" },
});
}
function escapeXml(value: string) {
return value.replace(/[<>&'"]/g, (char) =>
({ "<": "<", ">": ">", "&": "&", "'": "'", '"': """ })[char]!
);
}Escaping is not optional — one ampersand in a post title produces a feed that every reader rejects as malformed.
Then declare it in the root layout so it is discoverable from any page:
export const metadata: Metadata = {
alternates: {
types: { "application/rss+xml": "/rss.xml" },
},
};On a different stack? The general version of this fix explains what 13x checks and why it matters, without assuming a framework.
Check your Next.js (App Router) site
113 deterministic checks against your live URL, in about 30 seconds. Framework detected from the response, so every fix comes back in the form your stack actually uses.
No signup. Results in 30 seconds.