Sitemap.xml and RSS Feed in Next.js App RouterTechnical SEO 2026

sitemap6 min readJuly 20, 2026

Author: DevStudio.it

TL;DR

Google indexes faster with a complete sitemap.xml and RSS for the blog section — crawlers do not have to guess new posts. In Next.js App Router you generate both as sitemap.ts and feed.xml/route.ts without WordPress plugins. For multilingual sites (pl/en/de) each sitemap URL gets hreflang alternates, and RSS publishes only posts for that locale. Deploy on DevStudioIT Cloud serves files from CDN cache — updated on every next build. Dynamic content (case studies from PostgreSQL in Branchly) enters the sitemap via async fetch in sitemap.ts.

Who is this for

  • Next.js corporate sites with a blog and multiple language versions
  • SEO teams wanting technical control without Yoast / Rank Math
  • Projects migrating from WordPress — RSS subscribers need a new feed URL
  • Developers using ISR or static generation — sitemap must reflect revalidate
  • Anyone with a /sitemap.xml containing three URLs "because someone added them once"

Keyword

sitemap nextjs app router, rss feed next.js, feed.xml seo, sitemap.ts i18n, robots.txt nextjs 2026, hreflang sitemap

sitemap.xml — native MetadataRoute in Next.js

File app/sitemap.ts exports a function returning a URL array:

import type { MetadataRoute } from 'next';
import { getBlogPosts } from '@/lib/blog';

const BASE = 'https://devstudioit.com';

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const staticRoutes = ['', '/web-development', '/blog', '/contact'].flatMap((path) =>
    ['pl', 'en', 'de'].map((locale) => ({
      url: `${BASE}/${locale}${path}`,
      lastModified: new Date(),
      changeFrequency: 'weekly' as const,
      priority: path === '' ? 1 : 0.8,
    }))
  );

  const posts = await getBlogPosts();
  const blogEntries = posts.map((post) => ({
    url: `${BASE}/${post.locale}/blog/${post.slug}`,
    lastModified: new Date(post.updated ?? post.date),
    changeFrequency: 'monthly' as const,
    priority: 0.6,
  }));

  return [...staticRoutes, ...blogEntries];
}

Next.js serves the result at /sitemap.xml. Do not hand-write XML in public/ — you lose typing and dynamic entries.

Field Recommendation Note
lastModified Real content change date Not new Date() for static pages every build
changeFrequency weekly / monthly Crawler hint, not a guarantee
priority 0.5–1.0 relative Does not directly affect ranking

Multilingual — hreflang in sitemap vs &lt;head&gt;

Google accepts hreflang in sitemap (extension xhtml:link) or in HTML. Next.js Metadata API in layout:

export async function generateMetadata({ params }): Promise<Metadata> {
  const { locale, slug } = await params;
  return {
    alternates: {
      canonical: `https://devstudioit.com/${locale}/blog/${slug}`,
      languages: {
        pl: `https://devstudioit.com/pl/blog/${slug}`,
        en: `https://devstudioit.com/en/blog/${slug}`,
        de: `https://devstudioit.com/de/blog/${slug}`,
      },
    },
  };
}

Consistency between &lt;head&gt; and sitemap matters more than picking one method alone.

Split sitemap — when URL count > 50,000

Large sites split into an index:

File Content
/sitemap.xml Index pointing to sub-sitemaps
/sitemap-pages.xml Static pages
/sitemap-blog.xml Blog posts
/sitemap-cases.xml Case studies from Branchly

Google limit: 50 MB / 50k URLs per file.

RSS / feed.xml — route handler

RSS subscribers and aggregators expect application/rss+xml. Route handler:

// app/[locale]/feed.xml/route.ts
import { getBlogPosts } from '@/lib/blog';

export async function GET(
  _req: Request,
  { params }: { params: Promise<{ locale: string }> }
) {
  const { locale } = await params;
  const posts = await getBlogPosts(locale);
  const site = `https://devstudioit.com/${locale}`;

  const items = posts.slice(0, 20).map((post) => `
    <item>
      <title><![CDATA[${post.title}]]></title>
      <link>${site}/blog/${post.slug}</link>
      <guid isPermaLink="true">${site}/blog/${post.slug}</guid>
      <pubDate>${new Date(post.date).toUTCString()}</pubDate>
      <description><![CDATA[${post.description}]]></description>
    </item>
  `).join('');

  const xml = `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>DevStudio.it Blog (${locale})</title>
    <link>${site}/blog</link>
    <description>Web development guides and news</description>
    <language>${locale}</language>
    ${items}
  </channel>
</rss>`;

  return new Response(xml, {
    headers: {
      'Content-Type': 'application/rss+xml; charset=utf-8',
      'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400',
    },
  });
}

Final URLs: /pl/feed.xml, /en/feed.xml. Add &lt;link rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt; in blog &lt;head&gt;.

robots.txt — consistency with sitemap

File app/robots.ts:

import type { MetadataRoute } from 'next';

export default function robots(): MetadataRoute.Robots {
  return {
    rules: {
      userAgent: '*',
      allow: '/',
      disallow: ['/api/', '/admin/'],
    },
    sitemap: 'https://devstudioit.com/sitemap.xml',
  };
}
Mistake Effect
Sitemap points to staging URL Indexing test environment
disallow: /blog + active RSS Conflicting signals for Google
No sitemap in robots Slower discovery of new posts

Staging on DevStudioIT Cloud should have robots: noindex in layout metadata — separate instance, separate robots.

Dynamic Branchly content in sitemap

Case studies or CMS landings in PostgreSQL (Branchly):

const cases = await db.caseStudy.findMany({
  where: { published: true },
  select: { slug: true, updatedAt: true, locale: true },
});

const caseEntries = cases.map((c) => ({
  url: `${BASE}/${c.locale}/case-studies/${c.slug}`,
  lastModified: c.updatedAt,
}));

Cache: export const revalidate = 3600 in sitemap.ts. After CMS publish the sitemap refreshes within an hour without full redeploy — acceptable tradeoff for B2B blogs.

Validation before production

Test Tool
Sitemap XML validity Google Search Console → Sitemaps
RSS valid validator.w3.org/feed
hreflang Ahrefs / Screaming Frog
Feed returns 200 curl -I https://devstudioit.com/en/feed.xml

Submit sitemap in Search Console once — Google fetches XML updates automatically.

Workflow after publishing a new blog post

Typical cycle in a DevStudio project with markdown in repo:

  1. Merge PR with new file content/blog/{locale}/{slug}.md
  2. CI builds Next.js and deploys to DevStudioIT Cloud
  3. sitemap.ts at build adds URL with lastModified from frontmatter
  4. RSS route with revalidate: 3600 shows the post in feed within an hour (or immediately after build if SSG)
  5. Optionally ping Google Indexing API for critical landings — blog usually needs sitemap only
Step Owner Time
Content publish Content / dev PR review
Build + deploy CI 5–12 min
Google discovery Crawler 1–7 days (normal)
RSS for subscriber Feed reader On next poll

Do not mass-ping Indexing API for every post — daily quota and spam risk. Sitemap + internal linking from blog list is enough for B2B.

Technical SEO checklist at launch

  • /sitemap.xml returns 200 and includes all locales
  • /pl/feed.xml, /en/feed.xml, /de/feed.xml validate in W3C validator
  • robots.ts points to production domain, not staging
  • Each post has alternates.languages in metadata
  • Staging has noindex, production does not
  • Search Console: separate property or folder per locale (depending on structure)

FAQ

One RSS for all languages or separate per locale?

Separate per locale — clearer for subscribers and aligned with channel &lt;language&gt;. Optionally a main feed with Polish posts only if 90% of audience is PL.

Should sitemap include noindex pages?

Do not add URLs with robots: noindex — you waste crawl budget. Filter blog drafts and /preview pages.

RSS vs Atom — which to choose?

RSS 2.0 has widest compatibility. Atom (application/atom+xml) optionally alongside — most readers support RSS.

How often to rebuild sitemap with 100+ posts?

With markdown in repo the sitemap generates on every build — no problem. With CMS async fetch + revalidate is enough; full rebuild only on URL structure changes.

Want sitemap and RSS in your Next.js site?

Related posts

Google Search Console and Next.js indexing — SSR, SSG and sitemap in 2026
5 min read
JSON-LD and Schema.org — business website SEO in Next.js (2026 guide)
9 min read
Next.js App Router SEO: metadata, canonicals, hreflang – done right
6 min read

About the author

We build fast websites, web/mobile apps, AI chatbots and hosting setups — with a focus on SEO and conversion.

Recommended links

From theory to production — Branchly, our hosting stack and shipped work.

Like how we think? Let's build something together.

Start project configuration