Next.js Open Graph Meta Tags: The Complete Setup Guide

Add og:title, og:image, og:description and Twitter card tags to your Next.js app using the Metadata API, generateMetadata, and dynamic OG images.

The right way to set OG tags in Next.js 13+

Next.js 13 introduced the Metadata API — a typed, colocated way to define Open Graph tags per route. Instead of manually adding <meta> tags to a custom _document.tsx, you export a metadata object (or generateMetadata function) from each page.tsx or layout.tsx.

Static metadata export

For pages with fixed OG data, export a metadata const from your page file:

// app/page.tsx
import type { Metadata } from "next";

export const metadata: Metadata = {
  title: "Your Page Title",
  description: "Your page description here.",
  openGraph: {
    title: "Your Page Title",
    description: "Your page description here.",
    url: "https://yourdomain.com",
    siteName: "Your Site",
    images: [
      {
        url: "https://yourdomain.com/og.jpg",
        width: 1200,
        height: 630,
        alt: "Your OG image alt text",
      },
    ],
    type: "website",
  },
  twitter: {
    card: "summary_large_image",
    title: "Your Page Title",
    description: "Your page description here.",
    images: ["https://yourdomain.com/og.jpg"],
  },
};

Dynamic metadata with generateMetadata

For dynamic routes (blog posts, product pages), use generateMetadata to pull data per route:

// app/blog/[slug]/page.tsx
import type { Metadata } from "next";

interface Props {
  params: { slug: string };
}

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const post = await fetchPost(params.slug); // your data fetching

  return {
    title: post.title,
    description: post.description,
    openGraph: {
      title: post.title,
      description: post.description,
      url: `https://yourdomain.com/blog/${params.slug}`,
      type: "article",
      images: [
        {
          url: post.ogImage ?? "https://yourdomain.com/og-default.jpg",
          width: 1200,
          height: 630,
        },
      ],
    },
    twitter: {
      card: "summary_large_image",
      images: [post.ogImage ?? "https://yourdomain.com/og-default.jpg"],
    },
  };
}

Dynamic OG images with next/og

Next.js ships a built-in ImageResponse API (via next/og) that generates OG images at the edge from JSX. Create an image route:

// app/og/route.tsx
import { ImageResponse } from "next/og";

export const runtime = "edge";

export async function GET(req: Request) {
  const { searchParams } = new URL(req.url);
  const title = searchParams.get("title") ?? "Default Title";

  return new ImageResponse(
    (
      <div
        style={{
          width: "100%",
          height: "100%",
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          background: "#000",
          color: "#fff",
          fontSize: 60,
          fontWeight: "bold",
        }}
      >
        {title}
      </div>
    ),
    { width: 1200, height: 630 }
  );
}

// Then reference it in metadata:
// images: [{ url: `/og?title=${encodeURIComponent(post.title)}` }]

Common mistakes in Next.js OG setup

  1. Relative image URLs: images: [{ url: '/og.jpg' }] does not work — always use absolute URLs including the domain.
  2. Missing width/height in the images array: Next.js renders the meta tags but crawlers still benefit from explicit dimensions. Include width: 1200 and height: 630.
  3. metadata export in a Client Component: export const metadata only works in Server Components. Mark client files with 'use client' and move metadata to a parent server layout or page.
  4. og:url not set: always include url in the openGraph object so crawlers know the canonical URL for cache keying.
  5. Caching on deploy: social platforms cache OG data. After redeploying, use platform debugger tools to bust the cache.

Verify your Next.js OG tags

After deploying, view source on your live page and search for og:image. If you see it, you're set. If not, check that your route is a Server Component and that the absolute URL resolves publicly.

Want to verify your Next.js OG tags are rendering correctly? Paste your production URL into OG Fixer to see the live preview across Twitter, LinkedIn, Discord, and more.