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.
