Define storefront template types. Category pages, product listing pages, PDPs, brand pages, campaign pages, and guides each get their own metadata, schema, and internal-link defaults.
- Systematic metadata. With the Next.js Metadata API, title, description, canonical, and alternates can be generated consistently per route. That makes SEO for PDPs and product listing pages much more governable across thousands of URLs. Next.js documents generateMetadata() as the core API for route-level metadata generation.
- Typed sitemaps. Separate commerce sitemaps by type and market: categories, products, editorial, campaigns. Next.js also supports sitemap generation through its metadata file conventions.
- Curated internal links. Category → subcategory → product listing page → PDP and PDP → relevant guides or alternatives should be designed, not left to generic blocks. In larger storefronts, this directly affects crawl paths, discoverability, and money-page relevance.
- Performance is the gate. Google recommends strong Core Web Vitals for Search and user experience, and Search Console reports real-world field data. For commerce, category and PDP template groups should be monitored separately.
- Targets: LCP ≤ 2.5 s, INP ≤ 200 ms, CLS ≤ 0.1 on p75. For content-heavy and headless setups, see also our Next.js CMS solution and Next.js agency.
import type { Metadata } from "next";
type ProductPageProps = {
params: Promise<{
locale: "en" | "de";
slug: string;
}>;
};
async function getProduct(slug: string, locale: "en" | "de") {
return {
name:
locale === "de"
? "Performance Laufschuhe"
: "Performance Running Shoes",
metaTitle:
locale === "de"
? "Performance Laufschuhe – Leicht, stabil & schnell"
: "Performance Running Shoes – Lightweight, Stable & Fast",
metaDescription:
locale === "de"
? "Leichte Laufschuhe mit reaktionsfreudiger Dämpfung für tägliches Training und schnelle Einheiten."
: "Lightweight running shoes with responsive cushioning for daily training and faster sessions.",
};
}
export async function generateMetadata({
params,
}: ProductPageProps): Promise<Metadata> {
const { locale, slug } = await params;
const product = await getProduct(slug, locale);
const path =
locale === "de"
? `https://www.example.com/de/shop/${slug}/`
: `https://www.example.com/en/shop/${slug}/`;
const dePath = `https://www.example.com/de/shop/${slug}/`;
const enPath = `https://www.example.com/en/shop/${slug}/`;
return {
title: product.metaTitle,
description: product.metaDescription,
alternates: {
canonical: path,
languages: {
"de-DE": dePath,
"en-US": enPath,
"x-default": enPath,
},
},
openGraph: {
title: product.metaTitle,
description: product.metaDescription,
url: path,
type: "website",
},
};
}