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.xmlcontaining 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 <head>
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 <head> 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 <link rel="alternate" type="application/rss+xml"> in blog <head>.
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:
- Merge PR with new file
content/blog/{locale}/{slug}.md - CI builds Next.js and deploys to DevStudioIT Cloud
sitemap.tsat build adds URL withlastModifiedfrom frontmatter- RSS route with
revalidate: 3600shows the post in feed within an hour (or immediately after build if SSG) - 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.xmlreturns 200 and includes all locales -
/pl/feed.xml,/en/feed.xml,/de/feed.xmlvalidate in W3C validator -
robots.tspoints to production domain, not staging - Each post has
alternates.languagesin 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 <language>. 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?
- Contact us, we configure sitemap, hreflang, RSS, and Search Console for your multilingual blog
- JSON-LD Schema.org, technical SEO complement to sitemap
- DevStudioIT Cloud, hosting with cache for static XML
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.
