When you paste a link into a tweet, X should unfurl it into a card with a title, a description, and an image. The single setting that decides whether that image shows up large or as a cramped thumbnail is the Twitter card image size. Get the dimensions right and one image gives you a full-width preview on X and every other platform. Get them wrong and the card collapses to a small square or shows no image at all.
The short answer
For a large, full-width card on X, use 1200 by 628 pixels at a 1.91 to 1 aspect ratio. That is the size for a Summary Card with Large Image, the card type you almost always want.
A few details that matter:
- Aspect ratio is what X actually enforces. The card is 1.91 to 1. 1200 by 628 hits it; the near-identical 1200 by 630 Open Graph size renders the same way, so you do not need a separate image for X.
- Maximum 4096 by 4096 pixels, under 5MB. Above that, X will not render the image. Stay well under.
- Formats: JPG, PNG, or WebP. (Animated GIFs are not supported on cards.)
-
The small Summary Card is different. If you use
summaryinstead ofsummary_large_image, the image is a square thumbnail beside the text, minimum 144 by 144 pixels. This guide focuses on the large card.
Because 1200 by 628 is effectively the same 1.91 to 1 frame Facebook, LinkedIn, Discord, and Slack use, a single correctly sized image covers all of them.
How X builds a link card
X does not screenshot your page or guess at its content. When a URL is posted, X fetches the raw HTML and reads two families of meta tags from the <head>: the twitter: tags and, as a fallback, the Open Graph og: tags.
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Your Page Title" />
<meta name="twitter:description" content="A one-line summary of the page." />
<meta name="twitter:image" content="https://yourdomain.com/og.png" />
<!-- Open Graph fallbacks X also reads -->
<meta property="og:title" content="Your Page Title" />
<meta property="og:description" content="A one-line summary of the page." />
<meta property="og:image" content="https://yourdomain.com/og.png" />
The one tag with no Open Graph equivalent is twitter:card. It tells X which layout to use; summary_large_image is what gives you the 1.91 to 1 banner. For everything else, X falls back to the og: tag when the matching twitter: tag is missing. So in practice you can set twitter:card plus the four og: tags and skip the redundant twitter:title, twitter:description, and twitter:image. That is why the same image and tags that fix a LinkedIn link preview or a Discord link preview also fix the X card.
Two things trip people up:
- X reads the raw HTML, not the rendered page. It does not run your JavaScript. If your meta tags are set client-side (a single-page app that injects them after load), X never sees them. The tags must be in the HTML the server sends.
-
twitter:image/og:imagemust be an absolute, public URL. A relative path like/og.pngwill not resolve, and an image behind a login or a redirect X cannot follow renders as no image. Use the fullhttps://yourdomain.com/og.pngand confirm it loads in a private browser window.
Making the card image a real render of your page
The hardest part of a good card is not the four meta tags. It is producing an image at exactly 1200 by 628 that actually reflects the page, instead of a generic logo. For a marketing page, a docs page, or a launch announcement, the most compelling preview is often a clean shot of the page itself.
You can produce that with a screenshot API: point it at the URL, get back a hosted image at the size you ask for, and use that image as your og:image / twitter:image.
curl https://api.grabbit.live/v1/grabs \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourdomain.com/og",
"width": 1200,
"height": 628,
"format": "png",
"delay_ms": 500
}'
The response includes a hosted image_url:
{
"id": "grb_01jx...",
"status": "done",
"image_url": "https://cdn.grabbit.live/grabs/grb_01jx....png",
"width": 1200,
"height": 628,
"format": "png",
"bytes": 68820,
"execution_ms": 1180
}
width accepts 320 to 1920 and height 240 to 1080, so the 1200 by 628 card is in range. delay_ms (0 to 10000) gives client-rendered content or web fonts a moment to settle before the capture fires. Drop the returned image_url into your tags:
<meta name="twitter:image" content="https://cdn.grabbit.live/grabs/grb_01jx....png" />
<meta property="og:image" content="https://cdn.grabbit.live/grabs/grb_01jx....png" />
Capture once per unique page and cache the image_url alongside the page record rather than rendering on every request. A common pattern is to build a dedicated /og route with your normal components, capture that at 1200 by 628, and reuse the result everywhere. The same template-and-cache approach is covered in how to generate dynamic OG images from any URL.
How to validate the card
X retired its standalone Card Validator, so the old "paste a URL into the validator" workflow no longer exists. There are three reliable ways to check a card before it goes out (covered in depth in how to test your X link preview now that the validator is gone):
- Draft the tweet. Paste the URL into the X composer (or a test account) and look at the preview it renders. This is the ground truth, since it is X reading your live tags.
-
Inspect the tags directly. Run view-source on the live URL or
curlit and confirm thetwitter:cardandog:imagetags are present in the served HTML, before any JavaScript runs. -
Use a third-party Open Graph debugger. Because X reads the same
og:tags, a general Open Graph preview tool shows you how the card will look. Our OG image checker walks through previewing the image before you publish.
If the card looks wrong even though the tags are correct, the cause is almost always caching: X stored the metadata from the first time the URL was shared. Append a unique query string such as ?v=2 to the URL when you re-post, and X treats it as a new link with no cache entry.
Why your card still is not showing
If the tags are present, the image URL works in a browser, and you have re-posted with a cache-busting query string but the card still looks wrong, work through these:
-
The image is behind auth or a redirect. If
twitter:imagerequires a login, sits behind a paywall, or redirects in a way X will not follow, the card renders without an image. Test the raw image URL in a private window. - Tags are set by JavaScript. X reads static HTML and does not execute scripts. Server-render the meta tags.
- The image is the wrong shape or too small. Below the 1.91 to 1 ratio, X may show a small square thumbnail instead of a full-width banner, or fall back to the small card. Use 1200 by 628.
-
twitter:cardis missing. Withoutsummary_large_image, X defaults to the small summary card, so even a correctly sized image appears as a thumbnail. Set the card type explicitly.
Work top to bottom: confirm twitter:card is summary_large_image first, then verify the image is public and absolute, then bust the cache by re-posting with a fresh query string.
The short version
A Twitter (X) card is just your page's twitter: and Open Graph tags, rendered as a preview. Set twitter:card to summary_large_image, point og:image / twitter:image at an absolute, public 1200 by 628 image, and X gives you a full-width card that also covers Facebook, LinkedIn, Discord, and Slack. The validator is gone, so check the card by drafting a tweet or inspecting the served tags. If you want the image to be a real render of the page instead of a static logo, a screenshot API turns the live URL into the hosted image you point the tags at. If you are new to the format, start with what is an OG image.
Originally published on the Grabbit blog.
Top comments (0)