· Guides · By Simon Chiu

Twitter Card Image Size: summary vs summary_large_image

X card image sizes: 1200×600 (2:1) for summary_large_image, a square for summary. What 1200×630 and 1200×628 lose to the crop, and the tags that pick the card.

The Twitter (X) card image size depends on the card type. For summary_large_image use 1200×600 pixels, which is exactly the card's 2:1 shape. For summary, the small card, use a square image such as 600×600. Both must be under 5 MB. The card type comes from one tag, written with name, not property:

<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://example.com/cards/my-post-1200x600.png">
<meta name="twitter:image:alt" content="Title card for the post My post">

If you already serve a 1200×630 og:image, you can skip twitter:image: X falls back to og:image, and a centred 2:1 crop removes only 15 pixels top and bottom. The rest of this page shows when that crop matters, what the summary card does to a wide image, and how to render each shape from one template. The general Open Graph sizes for every other platform are in the OG image size guide.

Tested on 2026-10-18 with Node 22.18 on macOS. The crop numbers are computed (centred crop assumed). The card template was rendered with [email protected] Chromium at 1200×630, 1200×600 and 600×600 (no overflow at any size; the title hides in the square). The signing code below was run and reproduces the reference signature from our signed URL documentation. We did not post test cards to X for this guide, and X's developer documentation did not load for us that day, so the X limits below come from X's card documentation as last published; re-check them before you rely on the edge cases.

The two card types that use images

The Twitter card glossary entry lists all four types. For a website, the choice is between these two:

summary summary_large_image
Layout small thumbnail beside title and description full-width image above the title
Image shape 1:1 2:1
Size to render 600×600 (any square from 144×144 up) 1200×600
Minimum (X docs) 144×144 300×157
Maximum (X docs) 4096×4096, under 5 MB 4096×4096, under 5 MB
Formats (X docs) JPG, PNG, WEBP, GIF (first frame only) same
Good for docs pages, profiles, pages without a strong visual blog posts, launches, changelogs

SVG is not in X's list of formats. Use PNG for text-heavy cards and JPEG for photos.

What the 2:1 crop removes from common sizes

Most sites have one Open Graph image at 1200×630 and let X reuse it. Here is what a centred 2:1 crop removes from the common source sizes, and what a square crop removes when the same image lands in a summary card:

Source In the 2:1 large card In the 1:1 summary thumbnail
1200×600 nothing 300 px from each side
1200×628 14 px from top and bottom 286 px from each side
1200×630 15 px from top and bottom 285 px from each side
1200×675 (16:9) 37 to 38 px from top and bottom 262 to 263 px from each side
600×600 150 px from top and bottom nothing

Three practical rules follow:

  • 1200×630 is fine for the large card as long as nothing important sits in the top or bottom 15 pixels. That rules out thin border strips and footers pinned to the edge, which are common in card templates.
  • 1200×628 and 1200×630 behave the same. The twitter_card_large preset renders 1200×628 for teams that want the image to match the 1.91:1 shape exactly; the crop difference is one pixel per edge.
  • A wide image in a summary card loses about half its width. If you choose the small card, give it a square image designed for it, with the logo and first words in the middle.

One template, three shapes

You do not need three templates. Write the card with viewport units and flexbox instead of fixed pixel heights, and the same HTML renders correctly at 1200×630, 1200×600 and 600×600:

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<style>
  html, body { margin: 0; width: 100vw; height: 100vh; }
  body {
    box-sizing: border-box; display: flex; flex-direction: column; justify-content: center;
    gap: 2vh; padding: 6vh 6.5vw; background: #111827; color: #f9fafb;
    font-family: system-ui, sans-serif;
  }
  .site { font-size: max(18px, 4.4vh); color: #93c5fd; }
  h1 { margin: 0; font-size: max(28px, 10vh); line-height: 1.1; text-wrap: balance;
       display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; }
  /* Square thumbnails are tiny: show the site name only, larger. */
  @media (aspect-ratio: 1 / 1) { h1 { display: none; } .site { font-size: 14vh; text-align: center; } }
</style>
</head>
<body>
  <div class="site">example.com</div>
  <h1>Your post title, escaped on the server</h1>
</body>
</html>

The aspect-ratio media query is the useful trick here. A title that is readable in a 1200-pixel-wide card is unreadable in a thumbnail the size of a fingertip, so the square variant shows only the name or logo. Escape the title on the server before it goes into the HTML; the OG image generation guide explains why and has a tested escaping helper.

Render each shape with a preset

RenderScreenshot presets set the viewport for the two wide shapes. The square has no preset, so pass width and height directly. Each URL below is a signed URL, so it can sit in a public meta tag without exposing an API key.

Shape Request parameters Output
Open Graph, 1.91:1 preset=og_card 1200×630 PNG
X large card, exact 2:1 preset=twitter_card 1200×600 PNG
X large card, 1.91:1 preset=twitter_card_large 1200×628 PNG
X summary card, square width=600&height=600 600×600 PNG

Note that the twitter_card preset produces the 2:1 shape that summary_large_image uses, not the square thumbnail of the summary card.

The signing function is the Node.js listing from the signed URL documentation:

// sign.mjs
import { createHmac } from 'node:crypto';

// Same encoding as the API's signature check: space -> "+", only A-Z a-z 0-9 - _ . ~ kept.
const encode = (v) => encodeURIComponent(String(v))
  .replace(/[!'()*]/g, (c) => '%' + c.charCodeAt(0).toString(16).toUpperCase())
  .replace(/%20/g, '+');

export function signedScreenshotUrl(params, { keyId, secret, expiresAt }) {
  const fields = { ...params, key_id: keyId, expires: Math.floor(expiresAt.getTime() / 1000) };
  const query = Object.entries(fields).map(([k, v]) => `${encode(k)}=${encode(v)}`).sort().join('&');
  const signature = createHmac('sha256', secret).update(query).digest('hex');
  return `https://api.renderscreenshot.com/v1/screenshot?${query}&signature=${signature}`;
}

And the three image URLs for one post:

// cards.mjs: signed image URLs for one post in the three shapes X and Open Graph use.
import { signedScreenshotUrl } from './sign.mjs';

const keys = {
  keyId: process.env.RS_PUBLIC_KEY_ID, // rs_pub_...
  secret: process.env.RS_SECRET_KEY,   // rs_secret_..., server only
  expiresAt: new Date(Date.now() + 21 * 24 * 60 * 60 * 1000), // inside the 30-day maximum
};
const card = (shape) => `https://example.com/cards/my-post?shape=${shape}&v=1760745600`;
const cache = { cache_available: 'true', cache_ttl: 2592000 };

export const images = {
  og: signedScreenshotUrl({ url: card('wide'), preset: 'og_card', ...cache }, keys),        // 1200x630
  large: signedScreenshotUrl({ url: card('wide'), preset: 'twitter_card', ...cache }, keys), // 1200x600
  square: signedScreenshotUrl({ url: card('square'), width: 600, height: 600, ...cache }, keys),
};

for (const [shape, url] of Object.entries(images)) console.log(shape, url.replace(/signature=\w+/, 'signature=...'));

cache_available=true lets repeat crawler fetches inside the cache TTL reuse the first render for 0 credits; without it, every fetch is a new render. Each shape is a separate render, because the preset or viewport is part of the cache key, so serving both og:image and a dedicated twitter:image costs two renders per post instead of one. The expiry must be at most 30 days ahead; the caching reference and the signed URL page explain both rules.

Which tags to write

One image for everything (the default we recommend):

<meta property="og:image" content="{og url}">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="{description}">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image:alt" content="{description}">

A separate exact 2:1 image for X, when your design uses the full height:

<meta property="og:image" content="{og url}">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="{large url}">
<meta name="twitter:image:alt" content="{description}">

The small card with a square image:

<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="{square url}">
<meta name="twitter:image:alt" content="{description}">

In HTML attributes, a signed URL's & characters must be written as &amp;. Template engines that escape attribute values do this for you; string concatenation does not.

How X picks the image and title

X reads its own tags first and falls back to Open Graph for the rest. That fallback is why most sites need only one twitter: tag:

  1. Card type comes only from twitter:card. There is no Open Graph equivalent, so without this tag you do not get summary_large_image.
  2. Image comes from twitter:image, and if that is missing, from og:image.
  3. Title and description come from twitter:title and twitter:description, else from og:title and og:description.
  4. Alt text comes from twitter:image:alt. Set it explicitly; screen reader users on X hear it.

Because the image falls back, a page with a correct og:image and a twitter:card tag but no twitter:image is complete. Add twitter:image only for one of the two reasons above: a design that needs the exact 2:1 shape, or the square summary card.

To see the result before you share, compose a post with the link on X without sending it; the composer shows the card it will use. X no longer offers a separate card validator with a preview.

Troubleshooting X cards

Symptom Cause Fix
Small card instead of large twitter:card missing or set with property= <meta name="twitter:card" content="summary_large_image">
Title or footer cut off Content in the outer 15 px of a 1200×630 image Move it inward, or serve a 1200×600 twitter:image
Logo missing from the small card Wide image center-cropped to a square Serve a square image for summary
No image at all Image over 5 MB, SVG, or blocked for the crawler PNG or JPEG under 5 MB; check robots.txt for Twitterbot
Old image after a change The card is cached by URL Change the image URL (version parameter), then post the link again

If the image does not appear on any platform, the cause is usually not the size. Work through the twelve causes of a missing OG image, which comes with a script that checks each one. To render the three shapes from your own template, create a free account; it includes 150 credits a month.

Last tested 2026-10-18.

Related in this topic