Generating Dynamic Metadata: Next.js SEO for Dynamic Routes
Learn how to implement generateMetadata in Next.js to provide unique, SEO-friendly titles and descriptions for your dynamic blog routes.

Previously in this course, we covered Introduction to Dynamic Routes in Next.js for Blog Posts, where we set up the [slug] folder structure to render unique content based on the URL. While your content is now dynamic, your page titles and descriptions are likely still static, which hurts your search engine visibility.
In this lesson, we add generateMetadata to our dynamic routes. This allows us to fetch data for a specific post and inject it into the <head> of our document, ensuring Google and social media platforms display accurate information for every post.
Why Dynamic Metadata Matters for SEO
In Building the Root Layout: Metadata, Fonts, and HTML in Next.js, we defined static metadata for the entire site. However, search engines rank pages based on unique, descriptive content. If every blog post has the same title tag, search engines struggle to differentiate your content, leading to lower rankings.
By using generateMetadata, we can lift data from our database or CMS and reflect it in the browser tab and search results. This is crucial for SEO and social sharing (Open Graph images, Twitter cards).
Implementing generateMetadata

In the App Router, Next.js provides a special function called generateMetadata. If you export this function from a page.js or layout.js file, Next.js will call it before rendering the page, wait for it to resolve, and then populate the metadata.
Here is how you implement it in your dynamic app/blog/[slug]/page.js:
JAVASCRIPT// app/blog/[slug]/page.js // 1. Define your fetch logic(or reuse a helper) async function getPost(slug) { // In a real app, this would be a DB call return { title: CE9178">`Post about ${slug}`, description: "Read more here." }; } // 2. Export the function export async function generateMetadata({ params }) { const { slug } = await params; const post = await getPost(slug); return { title: post.title, description: post.description, }; } export default async function Page({ params }) { const { slug } = await params; return <h1>Post: {slug}</h1>; }
Key Rules for generateMetadata
- Async execution: It must be an
asyncfunction because you almost always need to fetch data. - Params access: It receives the same
paramsobject as yourpage.jscomponent. - Return object: It must return a metadata object (the same shape as the
metadataexport). - Caching: Next.js automatically memoizes the data fetch if you call the same function in both
generateMetadataand your page component. You don't need to worry about the performance cost of fetching the data twice.
Worked Example: Improving the Blog Project
Let’s advance our running blog project. Assuming you have a getPostBySlug function, here is how you would wire it up to ensure your SEO is production-ready.
JAVASCRIPT// app/blog/[slug]/page.js export async function generateMetadata({ params }) { const { slug } = await params; const post = await getPostBySlug(slug); if (!post) { return { title: CE9178">'Post Not Found' }; } return { title: post.title, description: post.excerpt, openGraph: { title: post.title, description: post.excerpt, type: CE9178">'article', }, }; }
Practice Exercise
- Open your
app/blog/[slug]/page.jsfile. - If you haven't already, ensure your
getPostBySlugfunction is exported from a data utility file. - Add the
generateMetadatafunction to your file. - Dynamically return the
titleanddescriptionfrom your data object. - Save and inspect the page source in your browser to verify that the
<title>and<meta name="description">tags have updated.
Common Pitfalls
- Forgetting to await params: In newer versions of Next.js,
paramsis a Promise. Ensure you useawait paramsbefore accessing properties likeslug. - Returning an incomplete object: If your fetch fails, make sure you return default metadata. Returning
undefinedcan cause issues with default metadata inheritance. - Over-fetching: While Next.js deduplicates requests, be mindful of complex database queries inside
generateMetadata. Keep them focused on the fields required for SEO.
FAQ
Does generateMetadata replace static metadata?
No, it complements it. The root layout provides defaults, and generateMetadata in your dynamic segments overrides or extends them.
Can I use client components in generateMetadata?
No. generateMetadata is a server-side-only function. It cannot use hooks like useParams or useState.
How do I test my metadata? Use the Open Graph Preview Tool or simply view the page source (Ctrl+U) to verify the meta tags are rendered correctly in the HTML.
Recap

We’ve successfully moved from static page titles to dynamic SEO management. By exporting generateMetadata, we ensure that every blog post is uniquely identified by search engines. This is a critical step in building a professional-grade application that ranks well and provides clear context to users.
Up next: Managing Shared UI with Layouts — we'll learn how to nest layouts to keep your blog sidebar and navigation consistent across your posts.
Work with me

Next.js Full-Stack Web App Development
A fast, SEO-ready full-stack web app built with Next.js 16 — from idea to deployed product, by an engineer who ships to production.

Next.js Website & Landing Page Development
A blazing-fast, SEO-optimized website or landing page in Next.js — the kind that loads instantly and ranks. Design-to-code, done right.
