How to Add Open Graph Images in Nuxt.js
Set a public Open Graph image in Nuxt 4 with useSeoMeta, add route-specific images, and fix previews that fail to appear.
In Nuxt 4, set the og:image metadata with useSeoMeta and give it an absolute image URL that social crawlers can fetch. Put a shared default in a shared layout or app component, and override it on pages that need a unique preview. For route data, use a reactive getter and make sure the metadata is available in the initial server-rendered HTML.
1. Add a shared Open Graph image
Place a static image in Nuxt’s public/ directory. Files in this directory are served from the site root, so public/og-image.png is available as /og-image.png. In production, use the full deployed URL in metadata so a crawler can request it independently of your page. See Nuxt’s public directory documentation.
For a site-wide default, add metadata in app.vue or a shared layout:
<!-- app.vue -->
<script setup lang="ts">
useSeoMeta({
ogTitle: 'My Nuxt site',
ogDescription: 'A short description of this page.',
ogImage: 'https://example.com/og-image.png',
twitterCard: 'summary_large_image',
})
</script>
<template>
<NuxtPage />
</template>
Replace https://example.com/og-image.png with the absolute URL for your deployed image. useSeoMeta maps ogImage to the Open Graph image tag and provides typed keys for common SEO fields. Nuxt recommends it for this purpose because it helps avoid mistakes such as using a name attribute where a property attribute is expected. Read the Nuxt useSeoMeta documentation and SEO and Meta guide.
2. Set a different image for a page
Call useSeoMeta in the page component to provide page-specific fields. Keep a shared default for routes that do not set their own image.
<!-- pages/about.vue -->
<script setup lang="ts">
useSeoMeta({
ogTitle: 'About our team',
ogDescription: 'Meet the people behind the product.',
ogImage: 'https://example.com/images/about-team.png',
twitterCard: 'summary_large_image',
})
</script>
<template>
<main>
<h1>About our team</h1>
</main>
</template>
Use one clear owner for each metadata field. Setting the same fields in useSeoMeta and useHead can make overrides harder to reason about. Inspect the final rendered HTML to confirm there is one effective og:image value.
3. Use route data for dynamic images
For pages whose image depends on fetched content, derive the URL from the page data and pass a getter to useSeoMeta. The getter keeps the metadata reactive if the data changes.
<!-- pages/posts/[slug].vue -->
<script setup lang="ts">
const route = useRoute()
const { data: post } = await useFetch(() => `/api/posts/${route.params.slug}`)
const imageUrl = computed(() =>
post.value?.socialImage ?? 'https://example.com/og-default.png'
)
useSeoMeta({
ogTitle: () => post.value?.title ?? 'Article',
ogDescription: () => post.value?.description ?? 'Read this article.',
ogImage: () => imageUrl.value,
twitterCard: 'summary_large_image',
})
</script>
<template>
<article v-if="post">
<h1>{{ post.title }}</h1>
<p>{{ post.description }}</p>
</article>
</template>
Adapt the endpoint and fields to your data source. Use a usable default while data is absent, and ensure the fetch can complete during server rendering. Nuxt notes that SEO metadata often does not need to be reactive because crawlers primarily scan the initial page load. A client-only update may therefore arrive too late for a crawler.
4. Choose the right Nuxt head API
| Need | Use | Notes |
|---|---|---|
| Common SEO fields such as title, description, and Open Graph image | useSeoMeta |
Typed keys; supports values and reactive getters. |
| Broader or custom head tags | useHead |
Use when you need head customization beyond the common SEO fields. |
| Static defaults for the whole app | app.head in nuxt.config.ts |
Suitable for fixed values; Nuxt documents that this configuration is not reactive. |
For example, use useHead if you need a custom tag alongside the standard metadata:
<script setup lang="ts">
useHead({
meta: [
{ name: 'theme-color', content: '#ffffff' },
],
})
useSeoMeta({
ogImage: 'https://example.com/og-image.png',
})
</script>
For shared static SEO defaults in configuration, Nuxt’s SEO and Meta guide covers head configuration and composables. Keep route-dependent values in composables where they can use page data.
5. Generate images when routes need custom cards
A regular public image is usually enough when every route can share one image. If many pages need their own title card or generated preview, consider the optional Nuxt OG Image module. The module listing describes built-in templates and Vue components, a Nuxt DevTools preview, rendering with Satori or Takumi, browser prerendering for complex templates, and page screenshots. Its listed installation command is:
npx nuxi@latest module add og-image
Generated images add a dependency and a rendering path to maintain. Choose the module when per-route image generation solves a real content or design need; it is not required to set a conventional og:image URL. Module features and versions can change, so consult the current module listing before adopting it.
6. Verify the deployed metadata and image
- Deploy the page and image to a publicly reachable HTTPS domain.
- Request the page HTML and inspect the rendered head for an
og:imagetag with the expected absolute URL. - Open the image URL directly, including in a private browser session. It should return the image without requiring a login, a development server, or browser-only state.
- Check the preview with the sharing debugger for the platform you care about. A correct tag does not guarantee identical previews across platforms.
- After changing an image, recheck using the platform’s available debugger. Preview caching behavior differs, and this research does not establish universal cache timing or image dimensions.
To inspect a page response from a terminal, use:
curl -L https://example.com/about
Search the returned HTML for property="og:image". This checks what the HTTP response contains; viewing the page after client-side JavaScript runs is not a substitute for checking the initial response a crawler may inspect.
7. Troubleshoot missing or incorrect previews
| Symptom | Likely cause | Fix |
|---|---|---|
| No image tag in the response HTML | Metadata is only set after client rendering, or the page component did not run for the route. | Set metadata in a server-rendered page or shared component. Make fetched data available during initial rendering where feasible, then inspect the deployed response. |
| Tag exists but the preview has no image | The image URL is relative, points to a local development host, or cannot be fetched by a remote crawler. | Use the absolute production URL and open it without authentication. Check the deployed image response. |
| A different page image appears | Shared defaults and page metadata overlap, or another head configuration overrides the field. | Choose a clear owner for the field and inspect the final rendered head for duplicate or unexpected tags. |
| The browser shows the new image but a share preview is old | The platform may be showing a cached preview. | Use that platform’s sharing debugger where available and check again after it refreshes. Cache timing varies. |
| The image URL works locally but fails in production | The file was not deployed at the expected public path, or the configured URL still points to a development host. | Confirm the file is in public/, deploy it, and test the exact production URL. |
| Metadata changes after navigation but not in a shared link | The metadata is updated only in the browser after the crawler’s initial fetch. | Render the route metadata on the server or include it in the initial HTML. |
8. Performance, reliability, and cost considerations
A static public image avoids generating a new asset for each request and is the simplest path to maintain. Reactive page metadata is useful when content determines the image, but server-rendering the relevant data helps make the result available in the initial HTML. A generator can support many unique cards, with the tradeoff of module configuration and image rendering to maintain.
There is no universal preview dimension, format, or cache lifetime established here. Choose an image appropriate for your design, make it reliably fetchable, and validate it with the target platform’s debugger. The Nuxt setup itself has no screenshot API cost. If you need to capture how a page renders as a visual asset for review or automation, ScreenshotNeo is a separate website screenshot API; it is not required to add the og:image metadata.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. A single GET request can return a PNG, JPEG, WebP, or PDF, which can help when you need a captured rendering of a page in addition to its Open Graph metadata. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/about -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com/about"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.com/about',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
Replace the example URL and provide your API key. The Python and Node.js snippets save the response body as an image; Node’s Bun.write requires Bun. With Node.js, use this alternative to save the image:
import { writeFile } from 'node:fs/promises';
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com/about' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture; those steps can be turned off. Bot checks, blank pages, and failed loads are never billed, and the response identifies the page verdict and billing status in headers. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for 1,000 free screenshots a month, with no card required.
FAQ
Does Nuxt need a special module to set an Open Graph image?
No. Use useSeoMeta with a publicly reachable image URL. The Nuxt OG Image module is optional for generated cards or page screenshots.
Can the image be different on every route?
Yes. Set page-specific metadata in each page component, or derive the URL from route data with a getter. Keep a default for routes without a custom image.
Does a correct og:image tag guarantee every platform shows the same preview?
No. Platforms can fetch and display previews differently. Check the target platform’s debugger and the actual deployed HTML and image URL.
Does this also cover Nuxt 3?
The versioned Nuxt 3 documentation shows the same general metadata pattern. Nuxt’s research-dossier documentation states that Nuxt 3 reached end of life on 31 July 2026; for new work, use the current Nuxt 4 documentation. See the Nuxt 3 SEO and Meta guide.


