Index of every page on this site: llms.txt

This guide covers upgrading from v6. If you use v5, follow the v6 migration guide first.

Update Node and Nuxt

Update your local, CI, and deployment environments before installing v7.

DependencySupported versions
Node.js`^22.22.3
Nuxt`^4.6.1
@unhead/vue^3.4.2
unstorage^1.17.5

Nuxt 3 and Nuxt 4 versions before 4.6.1 are no longer supported. If you pin @unhead/vue or unstorage directly or through an override, update those pins too. Nuxt 5 compatibility mode is optional. You do not need to enable it to use v7 on Nuxt 4.6.

From your application directory, update the module:

pnpm add nuxt-og-image@^7

Keep the packages required by your renderer. If you use Satori, update it and review your templates as described below.

Update optional peers

Update the optional peers your application already installs, including any dependency overrides.

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

See the font peer requirements in Update @nuxt/fonts.

Update Takumi

V7 requires Takumi 2.15.0 or later within v2. Takumi v1 is no longer supported. For native rendering, update the core package:

pnpm add @takumi-rs/core@^2.15.0

For Wasm rendering, update the Wasm package instead:

pnpm add @takumi-rs/wasm@^2.15.0

Replace takumi.fetchedResources and takumi.persistentImages with takumi.images. If you used both arrays, merge their entries into images. Each entry keeps its src and data fields.

Render your custom Takumi templates and compare their images before deploying. The renderer update can change text spacing and positioned layouts.

Update Zod for Nuxt Content

If you use defineOgImageSchema() from nuxt-og-image/content, update Zod to v4:

pnpm add zod@^4.6.6

Zod v3 is no longer supported. The Content integration remains optional. See the Nuxt Content integration for collection schema setup.

Update Satori templates

V7 requires Satori 0.47.1 or later within 0.47. Update it alongside Nuxt OG Image:

pnpm add -D satori@^0.47.1

Update your existing @resvg/resvg-js or @resvg/resvg-wasm package to 2.6.2 or later within v2. If you use UnoCSS, its @unocss/core and @unocss/config peers require 66.10.5 or later within v66. Update any older UnoCSS pins together.

Satori now supports block, inline, flexbox, and grid layout. The module no longer adds flex styles to elements that omit them. This can change existing .satori.vue templates and HTML passed to Satori.

Make flex layouts explicit

Previously, a bare <div> could receive display: flex and flex-direction: column automatically. Set both properties when your template needs that layout. For example, this v6 template relied on the automatic styles:

v6
<template>
  <div class="w-full h-full justify-between p-8">
    <h1>My article</h1>
    <div>example.com</div>
  </div>
</template>

In v7, declare the flex column:

v7
<template>
  <div class="box-border flex flex-col w-full h-full justify-between p-8">
    <h1>My article</h1>
    <div>example.com</div>
  </div>
</template>

For inline styles, use style="display: flex; flex-direction: column". If your template needs a row, set flex-direction: row explicitly. Check containers using gap, justify-content, or align-items without an explicit flex or grid display.

Text elements now use native text and inline layout. The module no longer adds flex wrapping or a synthetic 0.2em gap to headings, paragraphs, and inline elements. If your design relies on those styles, add flex flex-wrap gap-[0.2em] to the affected element. Otherwise, let Satori lay out the text and inline children naturally.

Preserve dimensions and spacing

Native CSS defaults can change the geometry of existing templates. Set the styles your design needs, then compare rendered images.

If the image changesWhat to check
Padding or borders make a fixed-size box largerAdd box-border, or set box-sizing: border-box
A border disappearsAdd border-solid, or set border-style: solid
Headings or paragraphs moveSet explicit margins, such as m-0, then add the intended spacing
An SVG or image shrinks inside a flex containerAdd shrink-0, or set flex-shrink: 0
A background image changes its crop or heightSet both dimensions of background-size explicitly

For example, background-size: 300% now preserves the background image's aspect ratio. If your old design used a stretched background, provide its intended height:

.profile-card {
  background-size: 300% 187px;
}

Use the height from your own template. The 187px value is an example from the module's fixture.

Review transforms and stacking

The module now passes translate, rotate, scale, and inset through to Satori. It also preserves z-index and backdrop utilities that earlier versions filtered out. Satori applies these properties natively.

If you combine individual transforms with transform, check their composed position, scale, and rotation. Check overlapping elements and backdrop effects, since previously ignored styles can now change the output. See Styling for supported layout and style options.

Compare images before deploying

Render every custom Satori template with representative text, images, emoji, and font weights. Compare both prerendered images and runtime images against your previous output. Use the rendered PNG for comparison; the HTML preview uses the browser's layout engine.

Fix layout differences with explicit template styles before accepting new snapshots. Keep your existing image comparison rules unless you intentionally change your project's visual tolerance.

Move signing to NUXT_APP_SECRET

V6 generated an OG image secret during each build unless you supplied one. V7 derives its signing key from Nuxt's private runtimeConfig.appSecret, using the purpose nuxt-og-image:url-signing. Production runtime requests require a root secret of at least 32 characters when signing is enabled. If the secret is missing or invalid, OG image requests return 500. Pages still render. The server logs the fix once per instance.

If your application already sets NUXT_APP_SECRET, keep that value. Otherwise, generate a root secret:

pnpm exec nuxt-og-image generate-secret

Set the printed NUXT_APP_SECRET value in your deployment environment. On Cloudflare Workers, configure it as a secret binding named NUXT_APP_SECRET. Keep the value private and stable across deployments and server instances within each environment.

V7 accepts NUXT_OG_IMAGE_SECRET when the application secret is empty. This fallback also supports Cloudflare bindings and logs a deprecation warning once per runtime. Rename the old environment key to NUXT_APP_SECRET when convenient. Keep its value unchanged. A non-empty application secret always takes precedence. Invalid values still fail closed. The fallback uses Nuxt's key derivation, so existing v6 signatures still become invalid.

Remove these old configuration settings if you use them:

  • String values of ogImage.security.secret
  • runtimeConfig.ogImage.secret

For example, remove the module-specific key:

nuxt.config.ts (v6)
export default defineNuxtConfig({
  ogImage: {
    security: { secret: process.env.NUXT_OG_IMAGE_SECRET },
  },
})

Configure the root secret through your environment instead:

deployment environment (v7)
NUXT_APP_SECRET=<your-generated-root-secret>

You do not need to declare runtimeConfig.appSecret in nuxt.config.ts. Never put it under runtimeConfig.public or app.config.ts.

Rendering modeRoot secret requirement
Production runtime, signing enabledSet it on every server instance
Strict prerenderingSet the same value during the build and at runtime
Normal static prerenderingNo root secret needed during prerendering
zeroRuntime: trueNo root secret needed unless strict prerendering produces signed URLs
DevelopmentNuxt generates and persists a development secret if none is configured

ogImage.security.secret: false still disables signing. It cannot be combined with security.strict: true. See Security before choosing unsigned runtime URLs.

Existing v6 signatures become invalid because the derived signing key differs from the old key. Rebuild prerendered pages and refresh cached HTML that contains dynamic OG image URLs. After upgrading, fetch a page's new og:image URL and confirm it returns an image. Changing NUXT_APP_SECRET later also invalidates signatures, sessions, and other values derived from that root secret.

If you purge images with ?purge=<token>, use the derived OG image signing key, rather than the root secret. If you use the legacy fallback, rename the environment key before using Nuxt's deriveSecret() directly. In server code, derive it with:

import { deriveSecret } from 'nuxt/server'

const purgeToken = await deriveSecret('nuxt-og-image:url-signing')

Keep this token private. On Workers, run administrative purge code where Nuxt can resolve the root secret. See Runtime Cache for CDN cache limits.

Update server imports

If your server handlers call getOgImageUrl, import defineEventHandler from nuxt/server. On Nuxt 4, the auto-imported h3 handler supplies a different event shape. The helper now accepts Nuxt's portable RequestEvent.

server/api/og-image.get.ts
import { defineEventHandler } from 'nuxt/server'
import { getOgImageUrl } from '#og-image/server'

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

Pass the current request event so signing can use request-specific bindings. For your own event annotations, import RequestEvent from nuxt/server instead of using H3Event.

App composables remain auto-imported. For explicit imports, use #og-image/app:

import { defineOgImage } from '#og-image/app'

Update @nuxt/fonts

Nuxt OG Image v7 reads fonts through the fonts:resolved hook of @nuxt/fonts. Use @nuxt/fonts 1.0.0 or newer, and update both modules together. If you install the optional font peers directly, use fontless 1.3.2+ and unifont 1.0.3+. Keep global: true on every font family used by an OG image.

pnpm add @nuxt/fonts@^1

If you pin the optional peers directly, update them too:

pnpm add fontless@^1.3.2 unifont@^1.0.3

V7 removes support for fontless 0.x and unifont 0.x. Remove overrides that pin either package to those versions.

If your @nuxt/fonts version is older than 1.0.0, OG images use the bundled Inter font. The build reports:

@nuxt/fonts <version> is not supported. Version 1.0.0+ is required for OG image font extraction.

OG images load the font files that @nuxt/fonts serves in development, prerendering, and runtime rendering. The webpack and rspack builders can use each configured family and weight. See Custom Fonts for configuration and renderer format support.

Check subsets and variable fonts

Configure every writing system your OG text needs in each family's subsets option. The default subsets do not include every writing system. For example:

nuxt.config.ts
export default defineNuxtConfig({
  fonts: {
    families: [
      { name: 'Noto Sans Devanagari', global: true, subsets: ['devanagari', 'latin'] },
    ],
  },
})

Avoid glyphs on families that render arbitrary OG text. It limits the served files to those characters. V7 attempts to resolve full alternatives. If that fails, it keeps the subset files and warns about missing characters.

Takumi accepts TTF, OTF, WOFF, WOFF2, and variable fonts. Satori still needs static fonts. V7 converts static WOFF2 files or resolves static alternatives for variable fonts. Keep configured providers available if your Satori fonts need these alternatives.

Install chrome-launcher for the browser renderer

V7 no longer installs chrome-launcher. Satori and Takumi apps need no change.

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.

Restore the DevTools panel

If you use the OG Image DevTools panel, install the optional host module:

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

Restart your development server after installation. The module loads the host automatically. You do not need to add it to modules. Production image rendering does not require it.

Check runtime cache storage

The default runtimeCacheStorage: true now uses a bounded 64 MiB memory cache when no Nitro cache mount is configured. It evicts the least recently used images when full. Requests for evicted images render them again. This limit covers stored image entries, rather than total server memory.

Configured Nitro cache mounts, named mounts, and explicit driver settings take precedence over this default. If you need persistence across server restarts, keep or configure a persistent driver.

Storage drivers now receive a TTL from cacheMaxAgeSeconds, with a minimum of 60 seconds. Drivers that support expiry can remove unread entries. Explicit memory storage still has no size limit in unstorage v1.17.5. Review custom driver limits in the Runtime Cache guide.

After upgrading, check a prerendered image, a signed runtime image, and each custom font family your templates use.

Was this page helpful?