Nuxt.js Open Graph Meta Tags: Complete Setup Guide

Add og:title, og:image, og:description, and Twitter card tags to Nuxt 3 apps using useSeoMeta, useHead, and dynamic metadata for blog posts and e-commerce pages.

Why Nuxt 3 is the right choice for OG tags

Nuxt 3 renders pages on the server by default, which means Open Graph meta tags are present in the HTML response before JavaScript runs. Social crawlers (Twitter, LinkedIn, Discord, Slack) never execute JS, so server-rendered OG tags are the only ones that actually work for link previews.

Nuxt 3 ships useSeoMeta — a composable that provides TypeScript-safe OG tag management with zero configuration. It's the recommended approach for all new Nuxt projects.

Basic setup with useSeoMeta

Call useSeoMeta at the top level of any page or layout component:

<!-- pages/index.vue -->
<script setup lang="ts">
useSeoMeta({
  title: 'My App — Fast Link Preview Checker',
  description: 'Check how your links appear on Twitter, LinkedIn, and Discord.',
  ogTitle: 'My App',
  ogDescription: 'Check how your links appear on Twitter, LinkedIn, and Discord.',
  ogImage: 'https://myapp.com/og/home.jpg',
  ogUrl: 'https://myapp.com',
  ogType: 'website',
  twitterCard: 'summary_large_image',
  twitterSite: '@myhandle',
  twitterTitle: 'My App',
  twitterDescription: 'Check how your links look before you share.',
  twitterImage: 'https://myapp.com/og/home.jpg',
})
</script>

<template>
  <main><!-- page content --></main>
</template>

Dynamic OG tags for blog posts

For content-driven pages where OG data comes from an API, combine useAsyncData with useSeoMeta:

<!-- pages/blog/[slug].vue -->
<script setup lang="ts">
const route = useRoute()

const { data: post } = await useAsyncData(
  `post-${route.params.slug}`,
  () => $fetch(`/api/posts/${route.params.slug}`)
)

useSeoMeta({
  title: () => post.value?.title ?? 'Blog',
  ogTitle: () => post.value?.title,
  ogDescription: () => post.value?.excerpt,
  ogImage: () => post.value?.coverImage,
  ogUrl: () => `https://myapp.com/blog/${route.params.slug}`,
  ogType: 'article',
  twitterCard: 'summary_large_image',
  twitterTitle: () => post.value?.title,
  twitterImage: () => post.value?.coverImage,
})
</script>

Using getter functions (() => value) makes the meta tags reactive — they update automatically when the data loads.

Global defaults in nuxt.config.ts

Set site-wide fallback OG tags in nuxt.config.ts so every page has baseline metadata even if a page-level override is missing:

// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      title: 'My App',
      meta: [
        { property: 'og:site_name', content: 'My App' },
        { property: 'og:type', content: 'website' },
        { property: 'og:image', content: 'https://myapp.com/og/default.jpg' },
        { name: 'twitter:card', content: 'summary_large_image' },
      ],
    },
  },
})

Generating dynamic OG images

Nuxt doesn't have a built-in OG image generator, but you can use nuxt-og-image (community module) or a Vercel Edge Function with @vercel/og to generate unique images per page:

// Install the module
npx nuxi module add nuxt-og-image

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['nuxt-og-image'],
  ogImage: {
    defaults: {
      component: 'OgImageDefault',
    },
  },
})

Common Nuxt OG tag pitfalls

  • Missing absolute URLs: all og:image and og:url values must start with https://.
  • SPA mode: if you set ssr: false in nuxt.config, OG tags won't be server-rendered. Remove it or use routeRules for hybrid rendering.
  • Wrong image dimensions: use 1200×630 px for maximum compatibility.
  • Stale platform cache: use each platform's scraping debugger after deploy.

Verify your Nuxt OG tags → Paste your URL into OGFixer to see exactly how your link looks on Twitter, LinkedIn, Discord, and Slack — before you share it.