Twitter Cards in Nuxt

Configure Twitter/X Cards to control how your links appear when shared on Twitter with rich previews, images, and metadata.
Harlan WiltonHarlan Wilton10 mins read Published Updated
What you'll learn
  • twitterCard is required. no fallback from OG tags for card type
  • Use summary_large_image for full-width previews (1200x630px recommended)
  • Twitter falls back to OG for title/description, but not for the card type itself

Twitter ignores most Open Graph tags and requires its own twitter:* meta tags for rich link previews. Without them, your links appear as plain text with no image or description.

<script setup lang="ts">
useSeoMeta({
  twitterCard: 'summary_large_image',
  twitterImage: '/social-preview.jpg',
  twitterTitle: 'Your page title',
  twitterDescription: 'Your page description'
})
</script>

Card Types

Twitter supports four card types via twitter:card:

summary_large_image - Full-width image preview (1200x630px). Use this for most pages.

useSeoMeta({
  twitterCard: 'summary_large_image',
  twitterImage: '/preview.jpg' // Minimum 300x157px, aspect ratio 2:1
})

summary - Small square thumbnail (1:1 aspect ratio). Don't use this unless you specifically want a smaller preview.

useSeoMeta({
  twitterCard: 'summary',
  twitterImage: '/icon.jpg' // Minimum 144x144px
})

player - Embedded video/audio player. Requires approval from Twitter.

app - Mobile app install prompts. Requires app store URLs and IDs.

Most sites should only use summary_large_image.

Required Tags

These three tags are mandatory for any Twitter Card:

useSeoMeta({
  twitterCard: 'summary_large_image',
  twitterTitle: 'Page title - 70 characters max',
  twitterImage: 'https://example.com/image.jpg' // Must be absolute URL
})

Image requirements:

  • Maximum 5MB file size
  • Supported formats: JPG, PNG, WEBP, GIF
  • summary_large_image: 2:1 ratio (1200x630px recommended)
  • summary: 1:1 ratio (300x300px minimum)
  • Must use HTTPS absolute URLs

Optional Tags

useSeoMeta({
  twitterCard: 'summary_large_image',
  twitterTitle: 'Your title',
  twitterImage: '/preview.jpg',
  twitterDescription: 'Description text - 200 characters max',
  twitterSite: '@yourhandle', // Your site's Twitter handle
  twitterCreator: '@authorhandle' // Content author's handle
})

twitterDescription defaults to og:description if not set, but specify it to control the exact text Twitter shows.

Open Graph Fallbacks

Twitter falls back to Open Graph tags when twitter:* tags are missing:

Twitter TagFallback
twitter:titleog:title
twitter:descriptionog:description
twitter:imageog:image

This won't work:

// ❌ Twitter will show nothing
useSeoMeta({
  ogTitle: 'My page',
  ogImage: '/image.jpg'
})

This works:

// ✅ Twitter uses og:* tags as fallback
useSeoMeta({
  twitterCard: 'summary_large_image', // Required - no fallback
  ogTitle: 'My page',
  ogImage: '/image.jpg'
})

Set both to avoid relying on fallback behavior:

// ✅ Explicit control
useSeoMeta({
  twitterCard: 'summary_large_image',
  twitterTitle: 'Twitter-specific title (70 chars)',
  twitterImage: '/twitter-preview.jpg',
  ogTitle: 'OpenGraph title can be longer',
  ogImage: '/og-preview.jpg'
})

Testing

Use our Social Share Debugger to preview how your links appear on Twitter, Facebook, LinkedIn, and other platforms.

You can also use Twitter Card Validator directly.

The validator caches results. If you update tags and don't see changes:

  1. Add a query parameter: ?v=2
  2. Wait 7 days for cache to expire
  3. Use a different URL to test

Common issues:

Image not showing - Check that:

  • URL is absolute with HTTPS
  • File size is under 5MB
  • Image exists and returns 200 status
  • No robots.txt blocking Twitter's bot

Wrong title/description - Twitter cached the old version. Add ?v=1 to the URL.

Card not appearing - You forgot twitterCard meta tag. It has no fallback.

Per-Page Cards

Set different cards for different pages:

<!-- ~/pages/blog/[slug].vue -->
<script setup lang="ts">
const { data: post } = await useAsyncData('post', () =>
  queryContent('blog', route.params.slug).findOne())

useSeoMeta({
  twitterCard: 'summary_large_image',
  twitterTitle: post.value.title,
  twitterDescription: post.value.excerpt,
  twitterImage: post.value.coverImage
})
</script>

Works best for blog posts with feature images, product pages with photos, landing pages with marketing visuals. Skip it for internal tools or password-protected content (Twitter can't fetch behind auth anyway).

Automated OG Image Generation

Manually creating images for every page is tedious. The Nuxt OG Image module generates them automatically at build time:

OG Image v5.1.13
2.9M
499
Generate OG Images with Vue templates in Nuxt.