Metadata API

Master the Next.js Metadata API — static and dynamic metadata, Open Graph, Twitter cards, JSON-LD, and the SEO patterns used in production applications.

Last reviewed: January 2025

·Next.js 13.2+Next.js 14Next.js 15
✅
Quick Understanding: The Metadata API replaces manual <head> tag management. Export a metadata object or generateMetadata function from any layout.tsx or page.tsx. Next.js automatically merges metadata from all levels of the layout hierarchy.

🔍 What Is It?

The Next.js Metadata API provides two ways to define <head> metadata:

  1. Static metadata — export a metadata object from a layout or page
  2. Dynamic metadata — export a generateMetadata async function that receives params

What it generates:

  • →<title> and <meta name="description">
  • →Open Graph tags (og:title, og:image, etc.)
  • →Twitter Card tags
  • →Canonical URLs
  • →Robots directives
  • →JSON-LD structured data
  • →Viewport and theme-color meta tags

🎯 Why Do We Need It?

SEO and social sharing are critical for most web applications:

  • →Search ranking — title and description directly affect click-through rates
  • →Social sharing — Open Graph tags control how links appear on Twitter/LinkedIn/Slack
  • →Rich results — JSON-LD structured data enables Google rich snippets

The Metadata API handles all of this declaratively, with TypeScript types ensuring you don't miss required fields.

🏗️ What Happens in Real Projects?

Metadata in a content platform:

  • →Root layout: site name, default description, OG image template
  • →Blog listing: "Blog | Site Name", blog-specific OG image
  • →Blog post: dynamic title, post excerpt as description, post hero as OG image
  • →Product page: product name, price in description, product image as OG
  • →404 page: "Not Found | Site Name", noindex robots directive

⚙️ How It Works

Metadata merging:

Next.js collects metadata from all layout and page files in the route hierarchy and merges them. Child metadata overrides parent metadata for the same fields.

app/layout.tsx         → title: "My Site"
app/blog/layout.tsx    → title: { template: "%s | My Site" }
app/blog/[slug]/page.tsx → title: "My Post"

Result: <title>My Post | My Site</title>

💻 Example

Metadata API Patterns — Static metadata, dynamic metadata, and Open Graph configuration.

ts
// app/layout.tsx
import type { Metadata } from 'next';

export const metadata: Metadata = {
  // Title template: child pages use %s
  title: {
    default: 'My SaaS Product',
    template: '%s | My SaaS Product',
  },
  description: 'The best SaaS product for enterprise teams.',
  // Canonical URL base
  metadataBase: new URL('https://myproduct.com'),
  // Open Graph defaults
  openGraph: {
    type: 'website',
    siteName: 'My SaaS Product',
    images: ['/og-default.png'], // Relative to metadataBase
  },
  // Twitter Card
  twitter: {
    card: 'summary_large_image',
    creator: '@myproduct',
  },
  // Robots
  robots: {
    index: true,
    follow: true,
    googleBot: { index: true, follow: true },
  },
};

🤔 Common Confusions

❓ Can I use generateMetadata in a layout?

Yes — and this is useful for section-level metadata. A blog layout can set the Open Graph type to "article" for all blog pages. A product layout can set the og:type to "product". Child page metadata overrides layout metadata for the same fields.

❓ What is metadataBase and why is it required?

metadataBase is the base URL of your site. It is required for resolving relative URLs in metadata (og:image, canonical). Without it, Next.js warns that relative URLs cannot be resolved. Set it to your production domain in the root layout.

❓ Can I use the Metadata API with the Pages Router?

No — the Metadata API is App Router only. In the Pages Router, use next/head to manually add <title> and <meta> tags. This is one of the key reasons to migrate to the App Router.

🚨 Common Production Issues

Missing metadataBase — relative OG image URLs are not resolved correctly, resulting in broken social share previews. Always set metadataBase to your production URL in the root layout.

generateMetadata not running for static pages — if you have export const dynamic = "force-static" and generateMetadata accesses dynamic data, it may not run correctly. Test metadata generation with next build.

OG images not updating — social platforms (Twitter, LinkedIn) aggressively cache OG images. After updating an OG image, use the platform's cache clearing tool (Twitter Card Validator, LinkedIn Post Inspector).

✅ Best Practices

Always set metadataBase in the root layout — required for OG images to work

Use title templates: { template: "%s | Site Name" } for consistent titling

Generate OG images dynamically with ImageResponse from next/og for per-page social images

Add JSON-LD structured data for articles, products, and FAQs to enable Google rich results

Set robots: { index: false } for draft content, admin pages, and thank-you pages

🏛️ Architect's Note

Senior Architect's Perspective

The Metadata API is one of the most compelling reasons to use the App Router. In the Pages Router, managing metadata across a large site with dynamic content required complex manual solutions. The App Router's declarative, hierarchical metadata system scales from a simple blog to a 10,000-page product catalog with minimal configuration.

💬 Frequently Asked Questions

How do I generate dynamic OG images?

Use ImageResponse from next/og in a special opengraph-image.tsx file (or opengraph-image.ts for programmatic generation). This generates an image using React JSX rendered to a PNG. Vercel's @vercel/og package is the same API.

How do I add JSON-LD structured data?

Add a <script type="application/ld+json"> tag in the page component (not in metadata). Example: const jsonLd = { "@context": "https://schema.org", "@type": "Article", name: post.title }; return <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />

Does generateMetadata affect page performance?

For static pages, generateMetadata runs at build time — no runtime cost. For dynamic pages, it runs on every request alongside the page component. Next.js deduplicates fetch calls between generateMetadata and the page component, so fetching the same data in both costs only one network request.

🎯 Key Takeaways

1Export metadata object or generateMetadata function from layout.tsx or page.tsx
2Metadata merges hierarchically — child overrides parent for the same fields
3Always set metadataBase in root layout for OG images to resolve correctly
4Use title templates for consistent page titling across the site
5generateMetadata is async — can fetch data for dynamic titles and descriptions
6Use ImageResponse in opengraph-image.tsx for dynamic social share images

🎯 Practice this topic in interviews

↗ Where this topic fits

Sources

Sources / References

Use these Next.js references to verify the concept in official docs and continue into the most relevant SplashRide pages.

Related SplashRide pages

SplashRide Next.js learning path

Browse the full Next.js topic collection and learning order on SplashRide.

Next.js Interview Prep

Practice Next.js interview questions and scenario-based answers.

Frontend Engineer Career Path

A beginner-friendly journey through web foundations, React, production frontend engineering, and interview preparation.

Full Stack Java Developer Career Path

A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.

Static Site Generation (SSG)

Master Static Site Generation in Next.js — pre-building pages at deploy time for maximum performance, CDN delivery, and zero server costs.

Image Optimization

Master next/image — automatic WebP conversion, lazy loading, CLS prevention, responsive images, and the configuration options for production image delivery.