twitterCard—there's no fallback from OG tagsSocial platforms use Open Graph (OG) and Twitter Card meta tags to generate link previews. Without them, shared links appear as plain text.
SSR required: Social crawlers don't run JavaScript. Your meta tags must be in the initial HTML response, which means server-side rendering or prerendering.
<script setup lang="ts">
useSeoMeta({
// Open Graph (Facebook, LinkedIn, Discord, Slack)
ogTitle: 'Your page title',
ogDescription: 'Short description',
ogImage: 'https://mysite.com/og.png',
ogUrl: 'https://mysite.com/page',
// Twitter/X (required - no OG fallback for card type)
twitterCard: 'summary_large_image'
})
</script>
og:title doesn't have to match your page title. Use casual, provocative language for social—it's not search.twitterCard: X/Twitter has no fallback for card type. Skip it and you get plain text.Use useSeoMeta() for OG tags—it handles the property attribute automatically.
useSeoMeta({
ogTitle: 'Hey! Open graph images are important.',
ogDescription: 'But who reads the description anyway?',
ogImage: 'https://mysite.com/og.png', // must be absolute URL
ogUrl: 'https://mysite.com/products/item', // canonical URL
ogType: 'website' // or 'article', 'product', etc.
})
| Spec | Value |
|---|---|
| Minimum size | 1200x630px (1.91:1 ratio) |
| Max file size | 5MB (1MB recommended) |
| Formats | JPG, PNG, WebP, GIF |
| URL | Absolute HTTPS |
Include dimensions for faster rendering:
useSeoMeta({
ogImage: 'https://mysite.com/og-images/preview.jpg',
ogImageAlt: 'Product preview showing 3 stacked boxes',
ogImageWidth: 1200,
ogImageHeight: 630,
ogImageType: 'image/jpeg'
})
/images/og.png won't work—social crawlers don't know your domain. Always include the full URL: https://mysite.com/images/og.pngFor blog posts, add article-specific tags:
useSeoMeta({
ogType: 'article',
articleAuthor: ['Harlan Wilton'],
articleSection: 'SEO Tutorials',
articleTag: ['vue', 'seo', 'meta-tags'],
articlePublishedTime: '2024-11-05T00:00:00Z',
articleModifiedTime: '2025-12-17T00:00:00Z'
})
X requires its own twitter:* meta tags. Without twitter:card, your links appear as plain text—no image, no description.
summary_large_image — Full-width image. Use this for most pages.
useSeoMeta({
twitterCard: 'summary_large_image',
twitterImage: '/preview.jpg' // 2:1 ratio, min 300x157px
})
summary — Small square thumbnail. For compact previews.
useSeoMeta({
twitterCard: 'summary',
twitterImage: '/icon.jpg' // 1:1 ratio, min 144x144px
})
player and app cards require approval from X.
X falls back to Open Graph when twitter:* equivalents are missing:
| Twitter Tag | Fallback |
|---|---|
twitter:title | og:title |
twitter:description | og:description |
twitter:image | og:image |
twitter:card has no fallback—skip it and you get no card.
Minimal setup using fallbacks:
useSeoMeta({
twitterCard: 'summary_large_image', // required
ogTitle: 'My page',
ogImage: 'https://mysite.com/image.jpg'
})
useSeoMeta({
twitterSite: '@yourhandle', // Site's X handle
twitterCreator: '@authorhandle', // Author's handle
twitterImageAlt: 'Alt text' // 420 chars max
})
Both platforms read Open Graph tags. No platform-specific tags needed.
useSeoMeta({
ogTitle: 'Dashboard Analytics',
ogDescription: '847 active users, 12.4% conversion rate',
ogImage: 'https://mysite.com/og/dashboard.png'
})
Slack also reads Twitter Card tags as fallback. Discord supports animated GIFs and shows real-time previews.
og:type, og:locale, article:* tags, Schema.org| Platform | Cache Duration | Clear Cache |
|---|---|---|
| 14-30 days | Sharing Debugger | |
| 7 days | Post Inspector | |
| X/Twitter | ~7 days | Add ?v=2 to URL |
| Slack | ~30 minutes | Add ?v=2 to URL |
| Discord | Real-time | No cache |
Data must be available during server render—onMounted runs too late for crawlers.
// entry-server.ts
export async function render(url: string) {
const product = await fetch(`https://api.mysite.com/product/${url}`).then(r => r.json())
const app = createSSRApp(App)
app.provide('product', product)
return { app, product }
}
<script setup lang="ts">
const product = inject<Product>('product')
useSeoMeta({
ogTitle: product?.name,
ogDescription: product?.shortPitch,
ogImage: `https://mysite.com/api/og?title=${encodeURIComponent(product?.name || '')}`,
twitterCard: 'summary_large_image'
})
</script>
Image not showing
Wrong title/description
?v=2 to URLtwitterCardCard not appearing at all
twitterCard for X (no fallback)Creating OG images manually is tedious:
Nuxt SEO handles social sharing automatically with the OG Image module.
Meta Description
Meta descriptions get rewritten by Google 70% of the time anyway. Here's how to implement them properly in Vue using composables and let your content do the heavy lifting.
Schema.org
Learn how to implement Schema.org structured data in Vue using Unhead. Get rich results in Google search with type-safe JSON-LD markup.