Headless Sitecore gives you rich page data; Next.js App Router gives you a structured way to turn that into HTML metadata. The two fit together well once you know where metadata is defined, what Next.js actually renders, and which CMS fields you want to own
Why not drop raw <head> tags in layout.tsx?
In the App Router, the supported approach is the Metadata API: export metadata (static) or generateMetadata (async, data-driven) from layout.tsx or page.tsx. Next merges metadata down the tree and emits the right <title>, <meta>, and link tags.
Manual <head> markup is easy to get out of sync with caching, streaming, and nested layouts. Reserve it for edge cases; for titles, descriptions, Open Graph, and robots, prefer metadata / generateMetadata.
Where does Sitecore fit in?
Your page content usually comes from getPage (or preview/design-library variants) via the Sitecore client. The route (or equivalent layout object) carries fields—for example a Title field you already use in components.
Metadata should use the same source of truth as the visible page: read field values from the layout/route object you get from Sitecore and map them into the Metadata shape. That keeps tab title, H1 strategy (if separate), and share previews aligned with what authors edit.
The core pattern: generateMetadata on your catch-all page
For a typical XM Cloud / Content SDK setup you often have a single dynamic route, e.g. [site]/[locale]/[[...path]]. That file is the right place to implement generateMetadata, because:
You have params (site, locale, path[]) for the public URL shape.
You can call client.getPage(path, { site, locale }) (or your cached wrapper) with the same inputs as the page component. You return an object Next understands: at minimum title, plus description, openGraph, twitter, alternates.canonical, etc.
Example shape (illustrative):
Tailor field names to your templates; the pattern is stable: fetch once (per request/build rules), map fields → Metadata.
Building canonical and Open Graph URLs
Crawlers and social platforms want absolute URLs. Set metadataBase (often in the root or site layout) to your public origin, e.g. from NEXT_PUBLIC_* env vars. For path segments, derive the pathname from params, not from client router APIs:
App Router’s useRouter() does not mirror Pages Router’s asPath.
generateMetadata is not a React component—you cannot use hooks there.
A robust approach: build something like /${site}/${locale}/${(path ?? []).join("/")} (with your trailing-slash rules), then combine with metadataBase or a single env base URL for openGraph.url and alternates.canonical.
Preview, drafts, and parity with the live page
If authors use preview or draft mode, decide whether generateMetadata should call the same preview-aware fetch as the page. If metadata always uses published getPage while the body uses preview, titles and OG tags can diverge from what authors see. Aligning fetch strategy (where possible) avoids “wrong title in share debugger” confusion.
What Next.js will not emit
Only known keys on the Metadata type become tags. Arbitrary properties (e.g. a custom itemID) are ignored—they will not appear in “View source.” For custom tags, use the documented escape hatches (e.g. other, depending on your Next version) or small, documented extensions, not ad-hoc top-level fields.
TypeScript and RouteData
SDK types for route may not list every runtime property. Prefer documented fields (fields, etc.). For extras, narrow safely or extend types in your app—avoid assuming route.siteName or route.url exist on the type unless your SDK version documents them.
References:

No comments:
Post a Comment