afaqrashid.com  ·  portfolio · 2026 ed.

Next.js X (Twitter) Card Not Working? 5 Things I Tested

If a Next.js page shows a small X card when you wanted the large image, check whether that page sets its own twitter object. When it does and doesn't list images, Next.js sets twitter:card to summary, even though it still adds the image.

I built a fresh Next.js 16.4.4 app, ran next build, and read the tags each page shipped with. Here's what I found, with the exact output.

The test setup

One root layout with a share image and the large card type, which is how most sites start:

// app/layout.tsx
export const metadata: Metadata = {
  metadataBase: new URL("https://example.com"),
  title: "Layout title",
  description: "Layout description",
  openGraph: {
    title: "Layout OG",
    description: "Layout OG desc",
    images: "/layout-og.png",
  },
  twitter: { card: "summary_large_image", site: "@example" },
};

1. A page's twitter object turns the large card into a small one

I gave one page its own X title and nothing else:

// app/c/page.tsx
export const metadata = { twitter: { title: "C tw" } };

The page shipped with this:

<meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="C tw"/>
<meta name="twitter:description" content="Layout OG desc"/>
<meta name="twitter:image" content="https://example.com/layout-og.png"/>

Two things went wrong. The card dropped from summary_large_image to summary, which is the small card with a square thumbnail. And twitter:site disappeared.

Metadata merges shallowly, so the page's twitter object replaced the layout's whole object, card and site included. Next.js then picked the card type itself: summary when the twitter object has no images of its own, summary_large_image when it does. The inherited image didn't count.

The fix is to set the card on any page that sets twitter:

export const metadata = {
  twitter: { card: "summary_large_image", title: "C tw" },
};

Listing images also worked. A page with twitter: { title, images } came out as summary_large_image.

2. You may not need a twitter object at all

Next, I removed twitter from the layout completely and left only openGraph. Every page still got a large card:

<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:title" content="Layout OG"/>
<meta name="twitter:description" content="Layout OG desc"/>
<meta name="twitter:image" content="https://example.com/layout-og.png"/>

With no twitter object, Next.js 16 copies the title, description and image from openGraph and picks summary_large_image when there's an image. X can also read Open Graph tags itself when the twitter ones are missing.

So the simplest setup is to keep openGraph right on every page and set at most twitter: { card: "summary_large_image" } in the layout. That's what this site does.

3. Every card has the layout's title

A page that set only title: "A" still shipped the layout's Open Graph title, and the X card copied it:

<meta property="og:title" content="Layout OG"/>
<meta name="twitter:title" content="Layout OG"/>

The page's title doesn't flow into openGraph.title when the layout has set one. Either leave openGraph.title out of the layout, or set openGraph on each page with its own title. The Open Graph post linked below shows a helper that keeps the shared fields in one place.

4. No image means no large card

A page that set openGraph: { title } without images replaced the layout's openGraph, image included. Its tags:

<meta property="og:title" content="K OG"/>
<meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="K OG"/>

No og:image, no twitter:image, and a small card. Spread the shared images into the page's openGraph, or add an opengraph-image file to the page's folder.

5. twitter-image only changes the X card

A twitter-image.png file in a page's folder set twitter:image, with its width, height and type, and left og:image on the layout's image. That's useful when you want a different picture on X.

An opengraph-image file is the better default, because Next.js copied it into the X tags too:

<meta property="og:image" content="https://example.com/d/opengraph-image.png?opengraph-image.0py7ao6vh72r1.png"/>
<meta name="twitter:image" content="https://example.com/d/opengraph-image.png?opengraph-image.0py7ao6vh72r1.png"/>
<meta name="twitter:image:width" content="1200"/>
<meta name="twitter:image:height" content="630"/>

If the tags are right and the card still doesn't show

Check that the image URL is absolute and not localhost. Without metadataBase, a self-hosted build points images at http://localhost:3000, which X can't load. The Open Graph post below covers that fix.

Then check robots.txt. If it blocks the image path or the page, X's crawler can't fetch them.

Check your own page

View the page source and search for twitter:card. If it says summary on a page that should have a large image, that page sets its own twitter object.

Or paste the source into the free metadata checker linked below. It flags a small card on a page that has an image, and a missing share image.

Related

Sources