← All lessons
Lesson 18 · Advanced

Metadata & SEO

Declare titles, descriptions, Open Graph and Twitter cards. Generate dynamic metadata per page.

Every page and layout can export a metadata object or a generateMetadata function. Next.js merges metadata from the root layout down to the leaf page, then renders the right tags in <head>.

Static metadata

// app/layout.js
export const metadata = {
  title: {
    default: "Acme",
    template: "%s · Acme",
  },
  description: "We sell calm software.",
  openGraph: {
    siteName: "Acme",
    images: ["/og-default.png"],
  },
};

The template field is the slick part. Child pages set just their title, and Next.js wraps it: Pricing → Pricing · Acme.

Per-page metadata

// app/pricing/page.js
export const metadata = {
  title: "Pricing",
  description: "Simple plans for every team.",
};

export default function PricingPage() { /* ... */ }

Dynamic metadata

When the title depends on data — a blog post, a product — export an async generateMetadata function. It receives params and searchParams (both Promises in Next.js 16).

// app/blog/[slug]/page.js
export async function generateMetadata({ params }) {
  const { slug } = await params;
  const post = await getPost(slug);
  return {
    title: post.title,
    description: post.summary,
    openGraph: {
      images: [post.coverUrl],
    },
  };
}

Generated images, sitemaps and robots

  • **opengraph-image.js** or twitter-image.js in a route folder render dynamic social cards using the ImageResponse API.
  • **sitemap.js** exports a function returning URLs — Next.js serves /sitemap.xml.
  • **robots.js** exports a robots config — Next.js serves /robots.txt.
// app/sitemap.js
export default async function sitemap() {
  const posts = await getAllPosts();
  return [
    { url: "https://example.com/" },
    ...posts.map((p) => ({
      url: `https://example.com/blog/${p.slug}`,
      lastModified: p.updatedAt,
    })),
  ];
}
Note: Set canonical URLs with alternates: { canonical: '...' } on pages that have duplicates or filters. It's the smallest SEO win you can ship.

Try it

Make this product page expose a dynamic title and description based on the loaded product.

Need a hint?

Export an async generateMetadata function that fetches the product and returns the title/description.

Quiz

Pick the best answer. You only get one shot per question.

1. Which export gives you a per-page title using a global suffix like `· Acme`?

2. How do you make metadata depend on the route's params (e.g., a post slug)?

3. Which special file generates a Next.js-served `/sitemap.xml`?