Open Graph tags in Next.js (App Router)

Next.js has a built-in Metadata API since the App Router — no more writing loose head tags, just export an object. It is the cleanest setup of any framework, as long as you know two pitfalls: metadataBase and the difference between static and dynamic metadata.

Want to see how your page looks right now?

The free checker shows your previews for nine platforms — from X and WhatsApp to iMessage, Discord and Bluesky.

Check your URL

Static: export metadata

In any layout.js or page.js (server component!) you export a metadata object with openGraph and twitter fields. Next automatically renders the right meta tags from it. See the example below.

Set metadataBase in your root layout

With metadataBase (e.g. new URL("https://yoursite.com")) relative URLs in openGraph.url and images are automatically made absolute. Forget it, and your og:image URLs stay relative — and scrapers ignore them.

Dynamic: generateMetadata

For pages with dynamic data (products, blog posts) export an async function generateMetadata({ params }) that returns the object based on your data. Note: in recent Next versions params is a Promise — await it first.

OG images via file conventions

Drop an opengraph-image.png (1200×630) next to your page.js and Next wires it up automatically. Want generated images (the title rendered into the image), use ImageResponse from next/og in an opengraph-image.js.

Code example

// app/layout.js — site-wide defaults
export const metadata = {
  metadataBase: new URL('https://yoursite.com'),
  title: 'Your site name',
  description: 'What your site does in one sentence.',
  openGraph: {
    title: 'Your site name',
    description: 'What your site does in one sentence.',
    type: 'website',
    url: '/',
    siteName: 'Your site name',
  },
  twitter: { card: 'summary_large_image' },
};

// app/blog/[slug]/page.js — dynamic per post
export async function generateMetadata({ params }) {
  const { slug } = await params;
  const post = await getPost(slug);
  return {
    title: post.title,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      type: 'article',
      url: `/blog/${slug}`,
      images: [{ url: post.ogImage, width: 1200, height: 630 }],
    },
  };
}

Common mistakes

Frequently asked questions

How do I make dynamic OG images (title rendered in the image)?

With ImageResponse from next/og in an opengraph-image.js file convention: you write JSX that is rendered to a PNG the moment a scraper requests the image.

Are layout and page metadata merged?

Yes — page metadata overrides layout metadata per field. Defaults in the layout, specifics per page.

Do my tags work with client-side navigation?

For scrapers only the server response matters, and with Next that is always correct — scrapers make a fresh request per URL.

Changed your tags? Verify the result.

Paste your URL and see within two seconds whether everything is right — on every platform.

Open the checker

Related guides

Deze gids in het Nederlands →