Repository navigation
Sitemaps
Status: planned / pre-release. This wiki page mirrors
docs/and documents the intended API of next-herald. Nothing is published yet; names may change before v1.0. See ROADMAP.
next-herald returns native MetadataRoute.Sitemap arrays for sitemap.ts and XML Responses only where Next has no API (ADR 0007).
herald routes scans app/ and writes herald-routes.gen.ts (ADR 0009):
export const routes = ['/', '/about', '/blog', '/blog/[slug]', '/docs/[...slug]'] as const;Route groups (marketing) are removed, @slot, intercepting (.) routes, _private folders, route handlers and metadata files are skipped. Run it in prebuild (herald build does) and commit or git-ignore the file.
defineSitemap(config, { routes, params, exclude, entry }):
- static routes are included automatically;
- every dynamic pattern needs a
paramsenumerator — a type error otherwise — or must be listed inexclude; -
entry(path, params)may returnlastModified,changeFrequency,priority,images,videos,alternates, ornullto skip; - URLs are absolute (from
siteUrl), deduplicated and sorted;trailingSlashis applied; - pages with
noindexmust be excluded by you;herald checkreportssitemap-noindexif one slips in.
Google uses lastmod only if it is "consistently and verifiably accurate" (Google), so next-herald never stamps build time (#50). Sources, best first:
- Your content's
updatedAtfromentry(); use the same value for JSON-LDdateModifiedandarticle:modified_time. -
herald routes --git-lastmod: the route file's last commit date (git log -1 --format=%cI), written toherald-routes.gen.tsat build time. Good for static pages; needs full history (not a shallow clone) and is wrong for data-driven pages. - Omit
lastModified.
herald check reports lastmod-future, lastmod-uniform (≥ 90% identical: build-time stamping) and lastmod-mismatch (sitemap vs JSON-LD/OG by more than a day).
Native: each entry accepts images: string[] and videos: [...] (Next renders image: / video: namespaces). Helpers sitemapImage() / sitemapVideo() validate required video fields (title, thumbnail_loc, description, content or player URL) per Google.
alternates: { languages: { de: '…', 'x-default': '…' } } per entry, generated for you when i18n is configured. See hreflang.
A sitemap may hold at most 50,000 URLs and 50 MB uncompressed (Google). Next's generateSitemaps splits but writes no index.
// app/sitemaps/sitemap.ts → /sitemaps/sitemap/0.xml, /1.xml, …
import { splitSitemap } from 'next-herald/sitemap';
import { source } from '@/lib/sitemap-source';
const s = splitSitemap(source, { pageSize: 50_000 });
export const generateSitemaps = s.generateSitemaps;
export default s.sitemap; // ({ id }) => entries; id is a Promise in Next 16// app/sitemap.xml/route.ts → /sitemap.xml (index)
import { sitemapIndex } from 'next-herald/sitemap';
import { source } from '@/lib/sitemap-source';
export const GET = sitemapIndex(source, { path: '/sitemaps/sitemap' });Do not keep app/sitemap.ts in this mode (vercel/next.js#87314). Avoid generateSitemaps under a dynamic segment until #98126 is fixed. source exposes count() and page(n, size) so chunks never load all URLs.
// app/news-sitemap.xml/route.ts
import { newsSitemap } from 'next-herald/sitemap';
export const GET = newsSitemap(herald, { publication: { name: 'Example Times', language: 'en' }, articles: getRecentArticles });Per Google News sitemaps: articles from the last 2 days only, max 1,000 URLs; older items are dropped with a dev warning.
sitemap.ts is a route handler. With Cache Components, wrap data access in "use cache" + cacheLife. Note upstream: revalidate in sitemap.ts may be emitted as static in adapter outputs (#99055); metadata routes send max-age=0 (#99715).
Guides
- Getting Started
- Configuration
- Metadata
- JSON LD
- Sitemaps
- Robots
- hreflang and i18n
- OG Images
- CLI and Check Rules
- Cache Components and PPR
- Static Export
- Testing
- AEO Stance
- SEO Preview Panel
- Redirects
- Pagination and Facets
- Image SEO
- Check Diff Mode
- Migration from next seo
- Migration from next sitemap
Reference