Titles that sell the click
SEO (search-engine optimization, from Unit 1) is the practice of making pages easy for search engines to read and rank — for a content site it decides whether anyone finds you at all. It starts embarrassingly simple: every page needs a good <title> and meta description. In Next.js you never touch <head> directly. You export a metadata object:
// app/about/page.js export const metadata = { title: "About us | Acme", description: "Meet the team building Acme.", }; export default function AboutPage() { … }
Next.js renders the tags into the HTML head on the server. And because Unit 1 taught you the HTML is real (not injected by client JavaScript), crawlers actually see it. This is a core reason content sites choose server rendering.
Dynamic pages need dynamic titles
/blog/[slug] cannot hardcode one title for every post. Export a generateMetadata function instead:
// 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 }; }
Decode it:
- Same
await paramsrule as the page itself. - It may fetch, and thanks to the request memoization from Unit 4, fetching the same post here and in the page costs one request, not two.
- Return shape is the same metadata object as the static version.
Title templates: brand every page once
Real sites suffix every title with the brand — 'Pricing | Acme', 'About us | Acme'. Typing "| Acme" on every page invites drift (someone forgets, someone writes - Acme). Metadata composes down the tree like layouts do, so the layout sets the pattern once:
// app/layout.js export const metadata = { title: { template: "%s | Acme", // %s is replaced by the child page's title default: "Acme", // used when a page sets no title }, };
Now a page exporting title: "Pricing" renders as <title>Pricing | Acme</title>, and a page with no title falls back to Acme. One definition, every page consistent — the same reasoning that made layouts better than copy-pasting a nav bar.
Quiz
The root layout sets title: { template: "%s | Acme", default: "Acme" }. The pricing page exports metadata = { title: "Pricing" }. What does the browser tab show on /pricing?
Quiz
Why do metadata exports beat setting document.title from a useEffect in a client component?
Problem
Every post at /blog/[slug] currently shows the site-wide title 'My Blog' in search results. Which function do you export from app/blog/[slug]/page.js to give each post its own title? (function name)