Why X shows a small card
X has two link card layouts, and twitter:card chooses between them. When the tag is missing, or its value isn’t one X recognizes, X uses the small summary card: a square thumbnail next to the title. It’s the only tag with no Open Graph equivalent, so pages that set only og: tags get the small card.
The values X accepts
summary_large_image: a wide 2:1 image across the post. Use this for almost every page. The image needs at least 300×157; 1200×600 or the standard 1200×630 both work.summary: a square 1:1 thumbnail beside the text, at least 144×144. Fine for profiles or pages with a logo instead of a picture.appandplayer: special layouts for apps and embedded media, with their own extra tags. Most sites don’t need them.
Values are lowercase with underscores. large_image, summary-large-image or a typo all count as invalid, and X falls back to the small card. The checker reports both a missing and an invalid value.
The other tags X reads
X falls back to og:title, og:description and og:image, so you don’t need twitter:title or twitter:image. Add twitter:image only for a different picture on X, because it takes priority. twitter:site (your @handle) is optional. The Twitter card validator previews both layouts for your page.
Still a small card?
- The image is too small or too heavy
- An image under 300×157 or over 5 MB can’t fill a large card. See why your preview image is small or cropped.
- There’s no usable image
- Without an image, X has nothing to show large. See why your link preview has no image.
- X still shows the old card
- X caches cards, reportedly for about a week, and has no public refresh tool. Post the URL with a harmless query string such as
?v=2to see your change. If you changed the image, give it a new URL too. - The tag is added by JavaScript
- X’s crawler doesn’t run JavaScript. See why your app’s links show a blank preview.
Code for your framework
These set twitter:card together with the Open Graph tags X falls back to. In Next.js and Nuxt the card is twitter.card and twitterCard.
HTML inside <head>
<title>How we cut our build time in half</title>
<meta name="description" content="What we changed in our CI pipeline, and what it saved.">
<link rel="canonical" href="https://example.com/blog/faster-builds">
<!-- Open Graph -->
<meta property="og:title" content="How we cut our build time in half">
<meta property="og:description" content="What we changed in our CI pipeline, and what it saved.">
<meta property="og:url" content="https://example.com/blog/faster-builds">
<meta property="og:type" content="article">
<meta property="og:site_name" content="Example Blog">
<meta property="og:locale" content="en_US">
<meta property="og:image" content="https://example.com/images/faster-builds.jpg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="A chart of build times dropping from 12 to 6 minutes">
<!-- X (Twitter). X falls back to the og: tags for title, description and image. -->
<meta name="twitter:card" content="summary_large_image">
Next.js (App Router) in page.tsx or layout.tsx
import type { Metadata } from "next";
// app/<route>/page.tsx (or layout.tsx)
export async function generateMetadata(): Promise<Metadata> {
return {
title: "How we cut our build time in half",
description: "What we changed in our CI pipeline, and what it saved.",
alternates: { canonical: "https://example.com/blog/faster-builds" },
openGraph: {
title: "How we cut our build time in half",
description: "What we changed in our CI pipeline, and what it saved.",
url: "https://example.com/blog/faster-builds",
type: "article",
siteName: "Example Blog",
locale: "en_US",
images: [
{
url: "https://example.com/images/faster-builds.jpg",
width: 1200,
height: 630,
alt: "A chart of build times dropping from 12 to 6 minutes",
},
],
},
twitter: {
card: "summary_large_image",
},
};
}
Nuxt in the page component
<script setup lang="ts">
useSeoMeta({
title: "How we cut our build time in half",
description: "What we changed in our CI pipeline, and what it saved.",
ogTitle: "How we cut our build time in half",
ogDescription: "What we changed in our CI pipeline, and what it saved.",
ogUrl: "https://example.com/blog/faster-builds",
ogType: "article",
ogSiteName: "Example Blog",
ogLocale: "en_US",
ogImage: "https://example.com/images/faster-builds.jpg",
ogImageWidth: 1200,
ogImageHeight: 630,
ogImageAlt: "A chart of build times dropping from 12 to 6 minutes",
twitterCard: "summary_large_image",
});
useHead({
link: [{ rel: "canonical", href: "https://example.com/blog/faster-builds" }],
});
</script>
React Router / Remix in the route module
// In a route module (React Router v7 framework mode or Remix v2).
export function meta() {
return [
{ title: "How we cut our build time in half" },
{ name: "description", content: "What we changed in our CI pipeline, and what it saved." },
{ tagName: "link", rel: "canonical", href: "https://example.com/blog/faster-builds" },
{ property: "og:title", content: "How we cut our build time in half" },
{ property: "og:description", content: "What we changed in our CI pipeline, and what it saved." },
{ property: "og:url", content: "https://example.com/blog/faster-builds" },
{ property: "og:type", content: "article" },
{ property: "og:site_name", content: "Example Blog" },
{ property: "og:locale", content: "en_US" },
{ property: "og:image", content: "https://example.com/images/faster-builds.jpg" },
{ property: "og:image:width", content: "1200" },
{ property: "og:image:height", content: "630" },
{ property: "og:image:alt", content: "A chart of build times dropping from 12 to 6 minutes" },
{ name: "twitter:card", content: "summary_large_image" },
];
}