Page metadata
@bison-lab/payload-core/metadata is what a page route imports. It never loads @payloadcms/plugin-seo, and nothing in it reaches React or next: pageMetadata returns a hand-written PageMetadata that is structurally assignable to Next’s Metadata.
The title template
Section titled “The title template”Next applies title.template to <title> alone, and pageMetadata needs the completed title for Open Graph, so both read one spelling:
import { titleTemplate } from "@bison-lab/payload-core/metadata";
export const metadata: Metadata = { title: { default: SITE_NAME, template: titleTemplate(SITE_NAME) },};The page route
Section titled “The page route”import { pageMetadata } from "@bison-lab/payload-core/metadata";
export async function generateMetadata({ params }): Promise<Metadata> { const page = await getPage(params); if (!page) return {}; return pageMetadata(page, { siteName: SITE_NAME, canonical: absoluteUrl(pagePath(page.slug)), absoluteUrl, });}pageMetadata returns <title>, description, canonical, Open Graph and Twitter with the share image (falling back to the original file when the rendition is missing), and noindex, nofollow when the page is hidden. A written meta title is absolute, so the layout’s template does not append the site name twice. Fields the editor left empty are omitted rather than set to undefined, so the layout’s own description and default share image carry through Next’s metadata merge.
The sitemap is the site’s: filter meta.noIndex out of it.
The package cannot import a site’s payload-types, so the shapes it reads (SeoMeta, SeoImageDoc, SeoPage) are hand-written and structural. A generated Page and Media are assignable to them; nothing carries an index signature.