Nuxt 3 Open Graph Meta Tags: useHead, useSeoMeta, and nuxt-og-image
How to add og:title, og:image, og:description, and Twitter card meta tags in Nuxt 3 using useHead, useSeoMeta, and the nuxt-og-image module for dynamic social preview generation.
Updated March 2026
Option 1: useSeoMeta (Recommended for Nuxt 3)
Nuxt 3's useSeoMeta composable is the cleanest way to set OG tags — fully typed with TypeScript and works with SSR out of the box:
<!-- pages/blog/[slug].vue -->
<script setup lang="ts">
const route = useRoute();
const { data: post } = await useAsyncData(route.params.slug as string, () =>
queryContent(route.params.slug as string).findOne()
);
useSeoMeta({
title: post.value?.title,
description: post.value?.description,
// Open Graph
ogTitle: post.value?.title,
ogDescription: post.value?.description,
ogImage: post.value?.ogImage ?? "https://yoursite.com/og-default.png",
ogUrl: `https://yoursite.com${route.fullPath}`,
ogType: "article",
// Twitter Card
twitterCard: "summary_large_image",
twitterTitle: post.value?.title,
twitterDescription: post.value?.description,
twitterImage: post.value?.ogImage ?? "https://yoursite.com/og-default.png",
});
</script>
<template>
<article>
<h1>{{ post?.title }}</h1>
<!-- content -->
</article>
</template>Option 2: useHead for More Control
<script setup lang="ts">
useHead({
title: "Your Page Title",
meta: [
{ name: "description", content: "Page description" },
// Open Graph
{ property: "og:title", content: "Your Page Title" },
{ property: "og:description", content: "Page description" },
{ property: "og:image", content: "https://yoursite.com/og.png" },
{ property: "og:url", content: "https://yoursite.com/page" },
{ property: "og:type", content: "article" },
// Twitter Card
{ name: "twitter:card", content: "summary_large_image" },
{ name: "twitter:title", content: "Your Page Title" },
{ name: "twitter:description", content: "Page description" },
{ name: "twitter:image", content: "https://yoursite.com/og.png" },
],
link: [
{ rel: "canonical", href: "https://yoursite.com/page" },
],
});
</script>Global Defaults in nuxt.config.ts
// nuxt.config.ts
export default defineNuxtConfig({
app: {
head: {
title: "My Site",
meta: [
{ name: "description", content: "My site default description" },
{ property: "og:type", content: "website" },
{ property: "og:image", content: "https://yoursite.com/og-default.png" },
{ property: "og:image:width", content: "1200" },
{ property: "og:image:height", content: "630" },
{ name: "twitter:card", content: "summary_large_image" },
],
},
},
})Per-page useSeoMeta or useHead calls override these defaults — they don't stack, they replace.
nuxt-og-image Module (Dynamic Generation)
For automatically generated OG images from a template (no manual image creation), use the community nuxt-og-image module:
# Install
npm install nuxt-og-image
# nuxt.config.ts
export default defineNuxtConfig({
modules: ["nuxt-og-image"],
ogImage: {
defaults: {
component: "OgImageDefault", // your component
width: 1200,
height: 630,
},
},
});Create a component in components/OgImage/Default.vue:
<!-- components/OgImage/Default.vue -->
<template>
<div class="og-wrapper">
<div class="brand">yoursite.com</div>
<h1>{{ title }}</h1>
<p>{{ description }}</p>
</div>
</template>
<script setup lang="ts">
defineProps<{
title?: string;
description?: string;
}>();
</script>
<style scoped>
.og-wrapper {
width: 1200px;
height: 630px;
background: #0a0a0a;
color: white;
display: flex;
flex-direction: column;
padding: 80px;
font-family: Inter, sans-serif;
}
</style>Then use it on any page:
<!-- pages/blog/[slug].vue -->
<script setup lang="ts">
defineOgImage({
component: "OgImageDefault",
title: post.value?.title,
description: post.value?.description,
});
</script>Nuxt 3 vs Nuxt 2 OG Tag Differences
| Approach | Nuxt 2 | Nuxt 3 |
|---|---|---|
| Per-page meta | head() option in component | useSeoMeta() / useHead() |
| Global defaults | nuxt.config.js head: | nuxt.config.ts app.head |
| Reactive meta | Manual via head() | Auto with useSeoMeta() |
| TypeScript support | Limited | Full |
| Dynamic OG images | nuxt-social-meta | nuxt-og-image module |
Verify SSR: Does Your OG Image Appear in Source?
# Check what Nuxt is server-rendering (must include OG tags) curl -s https://yoursite.com/blog/your-post | grep -E "og:|twitter:" # Expected output: # <meta property="og:title" content="..." /> # <meta property="og:description" content="..." /> # <meta property="og:image" content="..." /> # <meta name="twitter:card" content="summary_large_image" />
If OG tags don't appear in the curl output, your Nuxt app may not be SSR-rendering that page correctly. Check your rendering mode (ssr: true in nuxt.config.ts) and ensure useAsyncData resolves before the head is set.
Test your Nuxt 3 OG tags →
Preview my Nuxt OG tags →