Introduction

Nuxt OG Image v7 ships in Nuxt SEO v6. See the Nuxt SEO v6 release notes.

V7 requires Nuxt 4.6.1+ or Nuxt 5. It signs image URLs with your Nuxt app secret, uses native Satori layout, and reads fonts from @nuxt/fonts v1. It can also publish rendered images to object storage.

Follow the v6 to v7 migration guide to upgrade.

Quick migration:

pnpm add nuxt-og-image@^7
npx nuxt-og-image generate-secret

Set the printed NUXT_APP_SECRET value in your production environment.

Features

Publish Images to Object Storage

The module can upload rendered images to a public bucket through a Nitro storage mount. Prerendered og:image tags then use the bucket URL. (#730)

nuxt.config.ts
export default defineNuxtConfig({
  ogImage: {
    publish: { storage: 'og-image-public', baseURL: 'https://images.example.com' },
  },
  nitro: {
    storage: {
      'og-image-public': { driver: 'cloudflare-r2-binding', binding: 'OG_IMAGES' },
    },
  },
})

A runtime image request redirects to the published copy when it exists. Otherwise, the server returns the image and uploads it in the background. Prerendered local images now use app.cdnURL when you set it. See the publish option.

One Secret for Signing

URL signing now derives its key from the Nuxt 4.6 app secret. One NUXT_APP_SECRET value covers OG images, sessions, and other Nuxt features. (#729)

npx nuxt-og-image generate-secret
# NUXT_APP_SECRET=...

See the Security guide.

Native Satori Layout

Satori 0.47.1 supports block, inline, flexbox, and grid layout. Templates now use this native layout. (#736)

The module now passes translate, rotate, scale, inset, z-index, and backdrop utilities to Satori.

@nuxt/fonts v1 Integration

The module reads fonts through the fonts:resolved hook of @nuxt/fonts 1.0. (#698)

  • OG images can use every resolved family and weight, including families that only your site CSS uses.
  • Bold text works on the webpack and rspack builders.
  • Takumi loads OTF and WOFF files, as well as TTF and WOFF2.
  • Satori converts static WOFF2 files and resolves static alternatives for variable fonts.

Performance

  • SSR client builds no longer include the static URL encoder, the component manifest, or URL hashing. (#723)
  • On an edge benchmark, cold import CPU fell from 119.8 ms to 114.1 ms, and RSS fell from 50.8 MiB to 47.4 MiB. (#720)

Breaking Changes

Nuxt 4.6.1 and Node 22 Required

V7 drops Nuxt 3 and Nuxt 4 versions before 4.6.1. (#720, #739)

Dependencyv6v7
Nuxt>=3.16.0^4.6.1 || ^5.0.0
Node.js>=18.0.0^22.22.3 || ^24.15.0 || >=26.0.0
@unhead/vue^2.0.5 || ^3.0.0^3.4.2
unstorage^1.15.0^1.17.5

Signing Key From NUXT_APP_SECRET

V6 generated a new secret on each build unless you set one. V7 derives the key from Nuxt runtimeConfig.appSecret. (#729)

Production needs a root secret with at least 32 characters. If it is missing or invalid, OG image requests fail with a 500 error. (#742)

nuxt.config.ts
// Before: remove this
export default defineNuxtConfig({
  ogImage: { security: { secret: process.env.NUXT_OG_IMAGE_SECRET } },
})
deployment environment
# After
NUXT_APP_SECRET=<output of npx nuxt-og-image generate-secret>
  • V7 still reads NUXT_OG_IMAGE_SECRET when the app secret is empty, and warns once.
  • V7 removes string values of ogImage.security.secret and runtimeConfig.ogImage.secret. security.secret: false still turns off signing.
  • All v6 signed URLs become invalid. Rebuild prerendered pages and purge cached HTML.

Satori 0.47.1 Required

The satori peer is now ^0.47.1. The module no longer adds display: flex and flex-direction: column to a bare <div>. (#736)

<!-- Before -->
<div class="w-full h-full justify-between p-8">

<!-- After -->
<div class="box-border flex flex-col w-full h-full justify-between p-8">

Text elements no longer get flex wrapping or a 0.2em gap. Templates may also need box-border, border-solid, explicit heading margins, or shrink-0 on SVGs. The migration guide lists each layout change.

@nuxt/fonts 1.0 Required for Custom Fonts

With @nuxt/fonts before 1.0, OG images use the bundled Inter font and the build warns. (#698)

pnpm add @nuxt/fonts@^1

The fontless peer is now ^1.3.2, and the unifont peer is now ^1.0.3. If OG text uses a script outside the default subsets, set subsets on each family.

Optional Peers Updated and Takumi v1 Removed

V7 requires these optional peer ranges. (#739)

Packagev7 range
@takumi-rs/core, @takumi-rs/wasm^2.15.0
@resvg/resvg-js, @resvg/resvg-wasm^2.6.2
sharp^0.35.5
playwright-core^1.64.0
chrome-launcher^1.2.2
tailwindcss^4.3.3
@unocss/core, @unocss/config^66.10.5
zod^4.6.6

The Takumi fetchedResources and persistentImages options merge into images. Each entry keeps its src and data fields.

// Before
defineOgImage('Card', props, { takumi: { fetchedResources: [{ src, data }], persistentImages: [{ src, data }] } })

// After
defineOgImage('Card', props, { takumi: { images: [{ src, data }] } })

chrome-launcher Is an Optional Peer

The module no longer installs chrome-launcher. Satori and Takumi apps do not need it. (#749)

If you use the browser renderer with a local Chrome, install it as a dev dependency:

pnpm add -D chrome-launcher

Without it, development and local builds download Chromium through Playwright on the first render. The browser renderer still needs playwright-core. Without it, the build fails and prints the install command. This applies when you set ogImage.browser or a page uses a .browser.vue template.

Server Helpers Use Nuxt's RequestEvent

getOgImageUrl(event, ...) now takes Nuxt's portable RequestEvent. In a handler that calls it, import defineEventHandler from nuxt/server. (#720)

server/api/og-image.get.ts
// Before: the h3 defineEventHandler auto-import

// After
import { defineEventHandler } from 'nuxt/server'
import { getOgImageUrl } from '#og-image/server'

export default defineEventHandler(event => ({
  url: getOgImageUrl(event, '/blog/hello', { component: 'OgImagePost', props: { title: 'My post' } }),
}))

For explicit app imports, use #og-image/app.

DevTools Panel Needs nuxtseo-devtools-host

The OG Image DevTools panel moved to an optional host package. (#720)

pnpm add -D nuxtseo-devtools-host@^1

You do not need to add it to modules. Production rendering does not need it.

Bounded Runtime Cache

With the default runtimeCacheStorage: true and no Nitro cache mount, the image cache is now a 64 MiB LRU cache. (#726, #727)

  • runtimeCacheStorage: false now bypasses storage completely.
  • Storage drivers receive a TTL of cacheMaxAgeSeconds, with a minimum of 60 seconds. (#725)

See the Runtime Cache guide.

Bug Fixes

  • Escape commas and plus signs in dynamic image URLs, so a client cannot cut off the signature (#733)
  • Use the render timeout for storage lookups when publishing (#731)
  • Respect app.baseURL in DevTools font previews (#738)
  • Pass the cache TTL to the storage driver, by @0xgdr (#725)
  • Declare Site Config as a native module dependency (#734)
Was this page helpful?