---
title: "v6 to v7"
description: "Migrate Nuxt OG Image v6 to Nuxt OG Image v7."
canonical_url: "https://nuxtseo.com/docs/og-image/migration-guide/v7"
---

> Index of every page on this site: [llms.txt](https://nuxtseo.com/llms.txt)

This guide covers upgrading from v6. If you use v5, follow the [v6 migration guide](https://nuxtseo.com/docs/og-image/migration-guide/v6) first.

## Update Node and Nuxt

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

| Dependency                    | Supported versions |
| ----------------------------- | ------------------ |
| [Node.js](https://nodejs.org) | `^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:

```bash
pnpm add nuxt-og-image@^7
```

Keep the packages required by your [renderer](https://nuxtseo.com/docs/og-image/renderers).
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.

| Package                                | Required 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-nuxtfonts).

## 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:

```bash
pnpm add @takumi-rs/core@^2.15.0
```

For Wasm rendering, update the Wasm package instead:

```bash
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:

```bash
pnpm add zod@^4.6.6
```

Zod v3 is no longer supported. The Content integration remains optional.
See the [Nuxt Content integration](https://nuxtseo.com/docs/og-image/integrations/content) for collection schema setup.

## Update Satori templates

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

```bash
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](https://unocss.dev), 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>`{lang="html"} 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:

```vue [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:

```vue [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 changes                            | What to check                                                      |
| ----------------------------------------------- | ------------------------------------------------------------------ |
| Padding or borders make a fixed-size box larger | Add `box-border`, or set `box-sizing: border-box`                  |
| A border disappears                             | Add `border-solid`, or set `border-style: solid`                   |
| Headings or paragraphs move                     | Set explicit margins, such as `m-0`, then add the intended spacing |
| An SVG or image shrinks inside a flex container | Add `shrink-0`, or set `flex-shrink: 0`                            |
| A background image changes its crop or height   | Set 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:

```css
.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](https://nuxtseo.com/docs/og-image/guides/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:

```bash
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](https://cloudflare.com) 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:

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

Configure the root secret through your environment instead:

```bash [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 mode                      | Root secret requirement                                                |
| ----------------------------------- | ---------------------------------------------------------------------- |
| Production runtime, signing enabled | Set it on every server instance                                        |
| Strict prerendering                 | Set the same value during the build and at runtime                     |
| Normal static prerendering          | No root secret needed during prerendering                              |
| `zeroRuntime: true`                 | No root secret needed unless strict prerendering produces signed URLs  |
| Development                         | Nuxt 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](https://nuxtseo.com/docs/og-image/guides/security#disabling-signing) 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()`{lang="ts"} directly.
In server code, derive it with:

```ts
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](https://nuxtseo.com/docs/og-image/guides/runtime-cache#purging-the-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`.

```ts [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`:

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

## Update @nuxt/fonts

Nuxt OG Image v7 reads fonts through the `fonts:resolved` hook of [@nuxt/fonts](https://fonts.nuxt.com).
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.

```bash
pnpm add @nuxt/fonts@^1
```

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

```bash
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:

```txt
@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](https://webpack.js.org) and rspack builders can use each configured family and weight.
See [Custom Fonts](https://nuxtseo.com/docs/og-image/guides/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:

```ts [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:

```bash
pnpm add -D chrome-launcher
```

Without it, development and local builds download Chromium through [Playwright](https://playwright.dev) 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:

```bash
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](https://nuxtseo.com/docs/og-image/guides/runtime-cache).

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

## Sitemap

See the full [sitemap](https://nuxtseo.com/sitemap.md) for all pages.
