Skip to the solution
MediumFrontend5 min read

How does the Metadata API work in Next.js (static and generateMetadata)?

497 views

The problem

Question presented to candidate: "How do you set the page title, description and social tags in the App Router, including values that depend on data from a database?"

What a strong answer should cover:

  • Export a metadata object for static values, or an async generateMetadata function for values that depend on params or data.
  • Metadata is merged from the root layout down to the page; a title template in a layout wraps child titles.
  • metadataBase turns relative URLs (canonical, Open Graph images) into absolute ones; without it social image URLs fall back to localhost.
  • A fetch used in both generateMetadata and the page is memoized, so it runs once.
  • Since Next.js 15.2, metadata from a slow generateMetadata streams after the page for browsers, but blocks for HTML-limited bots so they still see it in the head.

Clarifying questions expected:

  • "Does the title depend on data?" β€” then generateMetadata, reusing the same data function as the page.
  • "Where will links be shared?" β€” Open Graph and Twitter tags matter for social previews.

Code / implementation expected: Yes β€” a layout with a template and a page with generateMetadata.

  • nextjs
  • metadata
  • seo
  • generatemetadata

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

5 min read Β· 8 sections

Read the code

A layout template and a data-driven page title
// app/shop/layout.tsx
import type { Metadata } from "next";
export const metadata: Metadata = {
  metadataBase: new URL("https://shop.example.com"),
  title: { template: "%s | Lab Shop", default: "Lab Shop" },
  openGraph: { siteName: "Lab Shop", type: "website" },
};

// app/shop/[slug]/page.tsx
import type { Metadata } from "next";
import { getProduct } from "@/lib/products"; // fetch or React.cache-wrapped query

export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
  const { slug } = await params;
  const product = await getProduct(slug);
  return {
    title: product.name,                                // -> "Trail Shoe | Lab Shop"
    description: product.summary,
    alternates: { canonical: `/shop/${slug}` },
    openGraph: { images: [product.imageUrl] },
  };
}

export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
  const product = await getProduct((await params).slug); // same call, memoized
  return <h1>{product.name}</h1>;
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 73 of 129 in Next.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Next.js questions
Up nextMedium

How do you enable and evaluate the React Compiler in Next.js 16?

Continue