Next.js cookbook · Beginner

Add SEO metadata, Open Graph and sitemaps

Give every page correct titles, descriptions, social previews and a sitemap for search engines.

Approach

Export static metadata in layouts and generateMetadata for dynamic pages (it can reuse cached data fetches). Add app/sitemap.ts and app/robots.ts, and opengraph-image.tsx for generated social images.

Solution

// app/layout.tsx
import type { Metadata } from "next";
export const metadata: Metadata = {
  metadataBase: new URL("https://shop.example.com"),
  title: { default: "Acme Shop", template: "%s · Acme Shop" },
  description: "Quality gear for developers.",
};

// app/products/[slug]/page.tsx
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
  const product = await getProductBySlug((await params).slug);   // deduplicated with the page's fetch
  if (!product) return { title: "Not found" };
  return {
    title: product.name,
    description: product.summary,
    alternates: { canonical: `/products/${product.slug}` },
    openGraph: { title: product.name, images: [{ url: product.imageUrl, width: 1200, height: 630 }] },
  };
}

// app/sitemap.ts
import type { MetadataRoute } from "next";
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const products = await listProductSlugs();
  return [
    { url: "https://shop.example.com", changeFrequency: "daily", priority: 1 },
    ...products.map((p) => ({ url: `https://shop.example.com/products/${p.slug}`, lastModified: p.updatedAt })),
  ];
}

Pitfalls

  • Duplicate titles across pages.
  • Missing canonical URLs for filtered/paginated variants.
  • Fetching the same data twice without caching (cache() / fetch dedupe).

Variations

  • JSON-LD structured data via a <script type="application/ld+json"> in the page.

Further reading: Next.js: Metadata and OG images

Reference: https://nextjs.org/docs/app/getting-started/metadata-and-og-images

More Next.js recipes