Guides

How to Generate Metadata in Next.js

The Metadata API lets every route define its own title, description, and Open Graph tags — either as a static object or dynamically, based on fetched data.

Prerequisites

  • arrow_rightA Next.js App Router project

Step 1: Static Metadata

app/about/page.tsx

import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'About | My Site',
  description: 'A short, specific description under 160 characters.',
};

Step 2: Dynamic Metadata

app/blog/[slug]/page.tsx

export async function generateMetadata({ params }: { params: { slug: string } }): Promise<Metadata> {
  const post = await getPost(params.slug);
  return {
    title: post.title,
    description: post.excerpt,
    openGraph: { title: post.title, description: post.excerpt, images: [post.image] },
  };
}

Step 3: Set a Site-Wide Default in the Root Layout

Metadata defined in app/layout.tsx acts as a fallback and can use title.template to append a consistent site name to every page's title automatically.

Common Errors

  • arrow_rightEvery page shows the same title — a static metadata export in a layout is overriding page-level metadata; check the specificity of where each is defined
  • arrow_rightgenerateMetadata not running — confirm it's exported from a page.tsx or layout.tsx file, not a regular component
  • arrow_rightOpen Graph image not showing on social platforms — confirm the image URL is absolute (starts with https://), not relative

Frequently Asked Questions

Can metadata be generated from a database?add

Yes — generateMetadata is an async function, so it can fetch the same data the page itself uses (ideally with request memoization so it's not fetched twice).

How long should a meta description be?add

Roughly under 160 characters so it doesn't get truncated in search results, though search engines sometimes rewrite descriptions regardless of length.

Do I need separate Open Graph and Twitter tags?add

Twitter reads Open Graph tags by default if twitter:card is set to summary_large_image; separate twitter: tags are only needed if you want Twitter-specific overrides.

BUILDING SOMETHING SIMILAR?

If you'd rather have this built and shipped than build it yourself, I'm available for freelance and contract work.