AgentScan logoAgentScan
Guides6 min read

Open Graph and Twitter Cards that render

Most social previews break for the same five reasons. The exact tags, dimensions, and validators to ship cards that survive Facebook, X, LinkedIn, and Slack.

Open Graph and Twitter Cards that render

Open Graph and Twitter Cards that render

Open Graph is the easiest piece of SEO metadata to get half-right. The hard part is shipping a card that survives every social platform without falling back to a default placeholder. This is the practical playbook.

The minimum tags that work

Eleven tags. Anything less and at least one platform breaks.

<title>Your page title (under 60 chars)</title>
<meta name="description" content="Your description, ideally 150-160 chars." />
<link rel="canonical" href="https://yourdomain.com/page" />

<meta property="og:type" content="website" />
<meta property="og:title" content="Your page title" />
<meta property="og:description" content="Your description" />
<meta property="og:url" content="https://yourdomain.com/page" />
<meta property="og:image" content="https://yourdomain.com/page/og.png" />
<meta property="og:site_name" content="Your Site" />
<meta property="og:locale" content="en_US" />

<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Your page title" />
<meta name="twitter:description" content="Your description" />
<meta name="twitter:image" content="https://yourdomain.com/page/og.png" />
<meta name="twitter:site" content="@yoursite" />

Build the block interactively with the Meta Tag Generator.

Image rules that get violated all the time

Most "the image isn't showing" tickets trace to one of these.

Use 1200x630

This is the universally supported size. 1.91:1 aspect ratio. Some platforms downscale, none of them upscale gracefully.

Stay under 5 MB

Some platforms cache only the first 5 MB of the URL. PNGs at this size should easily fit; if yours doesn't, your image is too detailed for a 200x100-display preview.

The URL must be absolute and HTTPS

Relative URLs and http:// URLs fail silently on some platforms. Always:

<meta property="og:image" content="https://yourdomain.com/og.png" />

Not /og.png. Not //yourdomain.com/og.png.

The URL must return HTTP 200 publicly

A common failure: the image is behind a CDN that requires a signed URL or a logged-in session. Social crawlers fetch unauthenticated. Test with a fresh incognito window or curl from a different network.

The URL must not redirect

Some crawlers follow redirects, some don't. Skip the question and serve the final image at its canonical URL.

og:url has to match canonical

If og:url and <link rel="canonical"> disagree, AI agents and SEO crawlers flag it. The two should always be identical and absolute. SEO audits will demote pages that get this wrong.

og:locale and og:site_name matter

Two fields that get dropped because the layout-level metadata gets shallow-merged away by the page-level override. In Next.js, this is the most common Open Graph bug.

When a page sets openGraph: { title, description }, the layout's siteName, locale, and other fields are dropped. Always include them in every page's openGraph block:

export const metadata: Metadata = {
  title: "About",
  description: "About our company.",
  openGraph: {
    title: "About | Your Site",
    description: "About our company.",
    url: "https://yourdomain.com/about",
    type: "website",
    siteName: "Your Site",
    locale: "en_US",
  },
};

If you only set title and description in openGraph, expect SEO crawlers to flag the missing fields.

Per-page OG images via file convention

Next.js 16 supports placing opengraph-image.tsx in any route segment. The framework auto-injects og:image, og:image:type, og:image:width, and og:image:height for that segment.

// app/blog/[slug]/opengraph-image.tsx
import { ImageResponse } from "next/og";

export const size = { width: 1200, height: 630 };
export const contentType = "image/png";

export default async function Image({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  return new ImageResponse(
    <div style={{ display: "flex" /* ... */ }}>{/* your composition */}</div>,
    size
  );
}

Pair with twitter-image.tsx to set twitter:image similarly. Most teams export both from the same source.

This pattern eliminates the per-page asset management that traditionally breaks at scale.

Twitter card types

Two of the four Twitter card types matter.

  • summary — small square thumbnail. Use for utility pages where the image is a logo.
  • summary_large_image — full-width hero. Use for blog posts and marketing pages. This is the modern default.

The other two (app, player) exist for app cards and video and rarely apply to content sites.

Validation

After you deploy, validate with three tools.

For Open Graph beyond just social, also run the page through Google's Rich Results Test since it surfaces JSON-LD issues that compound with OG bugs.

When the preview is stale

Social platforms cache aggressively. After publishing changes, you usually need to force a refresh:

  • Facebook / Meta: paste the URL into the Sharing Debugger and click "Scrape Again".
  • LinkedIn: paste into the Post Inspector. Refresh runs once per URL.
  • X: re-validate in the card validator. Some changes take 30 minutes to propagate.
  • Slack: there is no UI. The cache invalidates after ~24 hours, or you can prepend a fragment (#v=2) to force a fresh fetch.

Common mistakes summary

  • Using a relative og:image URL.
  • Setting only some Open Graph fields and losing layout-level fields to shallow merge.
  • Forgetting og:url or having it disagree with the canonical.
  • Image URL that requires auth or sits behind a redirect.
  • Image larger than 5 MB or too small to render at 1200x630.
  • og:type set to something exotic when website or article is what you actually mean.

What we ship at AgentScan

Every page on this site has its own opengraph-image.tsx plus a twitter-image.tsx that re-exports from it. The image renders the page title and description on a per-section gradient. The metadata block on each page sets siteName, locale, type, url, title, and description so nothing is lost to shallow merge.

If your site does not yet do this, the work is two hours and the cards become substantially more clickable. Build the tags with the Meta Tag Generator and the OG images with next/og's ImageResponse API.