3 · Reliability and delivery · 45 MIN
Project · A public course page with accurate metadata
Discoverable pages need meaningful HTML and resource-specific metadata.
A public course page should explain its prerequisites, syllabus and learning outcomes before asking a visitor to enroll. generateMetadata can derive a unique title and description from the same resource lookup used by the page. A canonical identifies the preferred public URL; it does not force indexing. Keep account pages private where appropriate and omit them from the public sitemap. This project uses a fixed trusted origin and validated known slugs rather than constructing canonical URLs from arbitrary request headers.
Inspect the document title and canonical for each published course.
Read the example
// app/courses/[slug]/page.tsx
import type {Metadata} from 'next';
import {notFound} from 'next/navigation';
const catalog={react:{title:'React foundations',summary:'Learn components, props, state and effect cleanup.'}};
type Props={params:Promise<{slug:string}>};
function find(slug:string){if(slug!=='react')notFound();return catalog.react}
export async function generateMetadata({params}:Props):Promise<Metadata>{
const {slug}=await params;const course=find(slug);
return {title:course.title,description:course.summary,alternates:{canonical:'https://example.com/courses/'+encodeURIComponent(slug)}};
}
export default async function Page({params}:Props){const course=find((await params).slug);return <main><h1>{course.title}</h1><p>{course.summary}</p><h2>Prerequisites</h2><p>JavaScript functions and arrays.</p><h2>Syllabus</h2><ol><li>Components</li><li>State</li><li>Effect cleanup</li></ol></main>}
Check the expected output
The React route contains a title, description, canonical and visible prerequisites and syllabus. Unknown slugs still use notFound.
Your challenge
Replace example.com with your project’s configured origin; add a sitemap containing only the published course URLs and verify server-rendered HTML.
Solution cost: O(1) lookup for this one-resource example. time · O(s) rendered syllabus content. space
Common trap
Metadata alone cannot make thin, inaccessible or blocked content rank.
Study the project implementation
// app/courses/[slug]/page.tsx
import type {Metadata} from 'next';
import {notFound} from 'next/navigation';
const catalog={react:{title:'React foundations',summary:'Learn components, props, state and effect cleanup.'}};
type Props={params:Promise<{slug:string}>};
function find(slug:string){if(slug!=='react')notFound();return catalog.react}
export async function generateMetadata({params}:Props):Promise<Metadata>{
const {slug}=await params;const course=find(slug);
return {title:course.title,description:course.summary,alternates:{canonical:'https://example.com/courses/'+encodeURIComponent(slug)}};
}
export default async function Page({params}:Props){const course=find((await params).slug);return <main><h1>{course.title}</h1><p>{course.summary}</p><h2>Prerequisites</h2><p>JavaScript functions and arrays.</p><h2>Syllabus</h2><ol><li>Components</li><li>State</li><li>Effect cleanup</li></ol></main>}
Further reading: Official documentation