---
title: "Social Sharing Meta Tags in Vue"
description: "Configure Open Graph and Twitter Card meta tags for rich link previews on Facebook, X/Twitter, LinkedIn, Slack, and Discord."
canonical_url: "https://nuxtseo.com/learn-seo/vue/mastering-meta/social-sharing"
last_updated: "2026-01-29"
---

<key-takeaways>

- OG tags control social previews on Facebook, [LinkedIn](https://linkedin.com), Slack, Discord
- Images must be absolute HTTPS URLs (not relative paths)
- Twitter/X requires `twitterCard`. there's no fallback from OG tags

</key-takeaways>

Social 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](/learn-seo/vue/routes-and-rendering/rendering) or prerendering.

```vue
<script setup lang="ts">
import { useSeoMeta } from '@unhead/vue'

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>
```

## Quick Tips

1. **Images matter**: Posts with optimized OG images see [40-60% higher click-through rates](https://ahrefs.com/blog/open-graph-meta-tags/#why-open-graph-tags-matter). Images must be absolute URLs, minimum 1200x600px.
2. **Social titles can differ**: Your `og:title` doesn't have to match your [page title](/learn-seo/vue/mastering-meta/titles). Use casual, provocative language for social. it's not search.
3. **Always set twitterCard**: X/Twitter has no fallback for card type. Skip it and you get plain text.

## Open Graph Tags

Use [`useSeoMeta()`](https://unhead.unjs.io/usage/composables/use-seo-meta) for OG tags. it handles the `property` attribute automatically.

### Core Tags

```vue
<script setup lang="ts">
import { useSeoMeta } from '@unhead/vue'

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.
})
</script>
```

### Image Requirements

<table>
<thead>
  <tr>
    <th>
      Spec
    </th>
    
    <th>
      Value
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Minimum size
    </td>
    
    <td>
      1200x600px (2:1 ratio)
    </td>
  </tr>
  
  <tr>
    <td>
      Max file size
    </td>
    
    <td>
      5MB (1MB recommended)
    </td>
  </tr>
  
  <tr>
    <td>
      Formats
    </td>
    
    <td>
      JPG, PNG, WebP, GIF
    </td>
  </tr>
  
  <tr>
    <td>
      URL
    </td>
    
    <td>
      Absolute HTTPS
    </td>
  </tr>
</tbody>
</table>

As of 2026, all major social platforms (Facebook, X/Twitter, LinkedIn, Discord, Slack) render 2:1 share images without cropping. The old 1.91:1 (1200x630) recommendation from Facebook's early docs is outdated. Use 1200x600 for clean 2:1 images.

Include dimensions for faster rendering:

```vue
<script setup lang="ts">
import { useSeoMeta } from '@unhead/vue'

useSeoMeta({
  ogImage: 'https://mysite.com/og-images/preview.jpg',
  ogImageAlt: 'Product preview showing 3 stacked boxes',
  ogImageWidth: 1200,
  ogImageHeight: 600,
  ogImageType: 'image/jpeg'
})
</script>
```

<warning>

OG images must use absolute URLs with HTTPS. Relative paths like `/images/og.png` won't work. social crawlers don't know your domain. Always include the full URL: `https://mysite.com/images/og.png`

</warning>

### Article Metadata

For blog posts, add article-specific tags:

```vue
<script setup lang="ts">
import { useSeoMeta } from '@unhead/vue'

useSeoMeta({
  ogType: 'article',
  articleAuthor: ['Harlan Wilton'],
  articleSection: 'SEO Tutorials',
  articleTag: ['vue', 'seo', 'meta-tags'],
  articlePublishedTime: '2024-11-05T00:00:00Z',
  articleModifiedTime: '2026-01-29T00:00:00Z'
})
</script>
```

## Twitter/X Cards

X requires its own `twitter:*` meta tags. Without `twitter:card`, your links appear as plain text. no image, no description.

### Card Types

**summary_large_image**: Full-width image. Use this for most pages.

```vue
<script setup lang="ts">
import { useSeoMeta } from '@unhead/vue'

useSeoMeta({
  twitterCard: 'summary_large_image',
  twitterImage: '/preview.jpg' // 2:1 ratio, min 300x157px
})
</script>
```

**summary**: Small square thumbnail. For compact previews.

```vue
<script setup lang="ts">
import { useSeoMeta } from '@unhead/vue'

useSeoMeta({
  twitterCard: 'summary',
  twitterImage: '/icon.jpg' // 1:1 ratio, min 144x144px
})
</script>
```

**player** and **app** cards require [approval from X](https://developer.x.com/en/docs/x-for-websites/cards/overview/player-card).

### X Fallbacks

X [falls back to Open Graph](https://developer.x.com/en/docs/x-for-websites/cards/overview/markup) when `twitter:*` equivalents are missing:

<table>
<thead>
  <tr>
    <th>
      Twitter Tag
    </th>
    
    <th>
      Fallback
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        twitter:title
      </code>
    </td>
    
    <td>
      <code>
        og:title
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        twitter:description
      </code>
    </td>
    
    <td>
      <code>
        og:description
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        twitter:image
      </code>
    </td>
    
    <td>
      <code>
        og:image
      </code>
    </td>
  </tr>
</tbody>
</table>

`twitter:card` has **no fallback**. skip it and you get no card.

Minimal setup using fallbacks:

```vue
<script setup lang="ts">
import { useSeoMeta } from '@unhead/vue'

useSeoMeta({
  twitterCard: 'summary_large_image', // required
  ogTitle: 'My page',
  ogImage: 'https://mysite.com/image.jpg'
})
</script>
```

### Optional Twitter Tags

```vue
<script setup lang="ts">
import { useSeoMeta } from '@unhead/vue'

useSeoMeta({
  twitterSite: '@yourhandle', // Site's X handle
  twitterCreator: '@authorhandle', // Author's handle
  twitterImageAlt: 'Alt text' // 420 chars max
})
</script>
```

## Slack & Discord

Both platforms read Open Graph tags. No platform-specific tags needed.

```vue
<script setup lang="ts">
import { useSeoMeta } from '@unhead/vue'

useSeoMeta({
  ogTitle: 'Dashboard Analytics',
  ogDescription: '847 active users, 12.4% conversion rate',
  ogImage: 'https://mysite.com/og/dashboard.png'
})
</script>
```

Slack also reads Twitter Card tags as fallback. Discord supports animated GIFs and shows real-time previews.

### Slack-Specific Behavior

- Cache: 7 days globally
- To refresh: use [Slack URL Debugger](https://api.slack.com/tools/debug/url) or add `?v=2` to URL
- Ignores: `og:type`, `og:locale`, `article:*` tags, Schema.org
- No redirect following for images. use direct URLs

## Platform Cache Times

<table>
<thead>
  <tr>
    <th>
      Platform
    </th>
    
    <th>
      Cache Duration
    </th>
    
    <th>
      Clear Cache
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Facebook
    </td>
    
    <td>
      14-30 days
    </td>
    
    <td>
      <a href="https://developers.facebook.com/tools/debug/" rel="nofollow">
        Sharing Debugger
      </a>
    </td>
  </tr>
  
  <tr>
    <td>
      LinkedIn
    </td>
    
    <td>
      7 days
    </td>
    
    <td>
      <a href="https://www.linkedin.com/post-inspector/" rel="nofollow">
        Post Inspector
      </a>
    </td>
  </tr>
  
  <tr>
    <td>
      X/Twitter
    </td>
    
    <td>
      ~7 days
    </td>
    
    <td>
      Add <code>
        ?v=2
      </code>
      
       to URL
    </td>
  </tr>
  
  <tr>
    <td>
      Slack
    </td>
    
    <td>
      7 days
    </td>
    
    <td>
      <a href="https://api.slack.com/tools/debug/url" rel="nofollow">
        Slack URL Debugger
      </a>
    </td>
  </tr>
  
  <tr>
    <td>
      Discord
    </td>
    
    <td>
      Real-time
    </td>
    
    <td>
      No cache
    </td>
  </tr>
</tbody>
</table>

## Dynamic Meta Tags

Data must be available during server render. `onMounted` runs too late for crawlers.

```ts
// 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 }
}
```

```vue
<script setup lang="ts">
import { useSeoMeta } from '@unhead/vue'
import { inject } from 'vue'

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>
```

## Testing Tools

1. [Facebook Sharing Debugger](https://developers.facebook.com/tools/debug/): Also validates OG tags for Slack
2. [LinkedIn Post Inspector](https://www.linkedin.com/post-inspector/)
3. X/Twitter: Compose a tweet (don't post) to see preview
4. Discord: Paste link for real-time preview

## Troubleshooting

**Image not showing**

- URL must be absolute HTTPS (not relative)
- File under 5MB
- Image returns 200 (not redirect)
- Not blocked by robots.txt

**Wrong title/description**

- Platform cached old version. add `?v=2` to URL
- For X: check you're setting `twitterCard`

**Card not appearing at all**

- Missing `twitterCard` for X (no fallback)
- Page requires auth (crawlers can't log in)

## OG Image Tools

Creating OG images manually is tedious:

- [OG Image Playground](https://og-playground.vercel.app/): Design and export with code
- [x-satori](https://github.com/Zhengqbbb/x-satori): Vue SFCs + Tailwind to generate at build time

## Using Nuxt?

[Nuxt SEO](/docs/nuxt-seo/getting-started/introduction) handles social sharing automatically with the OG Image module.

[Learn more about Social Sharing in Nuxt →](/learn-seo/nuxt/mastering-meta/open-graph)
