---
title: "Nuxt SEO Utils v9.0.0"
description: "Release notes for v9.0.0 of Nuxt SEO Utils."
canonical_url: "https://nuxtseo.com/docs/seo-utils/releases/v9"
---

## Introduction

Nuxt SEO Utils v9 supports Nuxt 4.6 and Nuxt 5. It ships in [Nuxt SEO v6](/docs/nuxt-seo/releases/v6).

This release removes support for Nuxt 3, Nuxt 4 before 4.6, and Unhead v2. Sharp is now an optional install. The module also warns when indexable pages render only on the client. The new `seo.redirects` option serves redirects and 410 responses for old URLs.

Check the [breaking changes](#breaking-changes) before you upgrade. Then follow the [migration steps](#migration-steps).

::note
Some features shipped in 8.x minor releases: Nuxt config head validation and `automaticTwitterTags` in 8.5.0, then route rule meta priority and the Agent Skill in 8.6.0. This page lists only the changes after v8.6.1.
::

## 🔍 Client Rendering Warnings

If SSR is off for a page, crawlers and agents that skip JavaScript can miss its content. v9 warns about these pages during `nuxt dev` and `nuxt build`. ([#170](https://github.com/harlan-zw/nuxt-seo-utils/pull/170))

The check reads the global `ssr` option and the final route rules. It skips API, proxy, redirect, and non-HTML routes. It also skips routes with `robots: false` and sites with `site.indexable: false`.

Each warning names the route and shows two fixes. For public pages, turn SSR back on:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  ssr: true,
  routeRules: { '/app/**': { ssr: true } },
})
```

For private app pages, keep client rendering and stop indexing with [Nuxt Robots](/docs/robots/getting-started/introduction):

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  modules: ['@nuxtjs/robots'],
  routeRules: { '/app/**': { robots: false } },
})
```

## ↪️ Redirects and Gone URLs

List the URLs you moved or deleted in `seo.redirects`. Each entry records the date you added it. ([#176](https://github.com/harlan-zw/nuxt-seo-utils/pull/176))

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  seo: {
    redirects: {
      '/blog/old-post': { to: '/blog/new-post', addedAt: '2026-10-13' },
      '/spring-sale': { statusCode: 410, addedAt: '2026-10-13' },
    },
  },
})
```

A moved URL gets a 301, or a 308. A gone URL gets your error page with a 410 or 404 status. The build flattens redirect chains and stops on loops.

No entry expires on its own. By default, `nuxt dev` and `nuxt build` list an entry for review after 365 days. See [Redirects and Gone URLs](/docs/seo-utils/guides/redirects).

## 📦 Typed Runtime Entry

Import the composables and their option types from `#seo-utils/app`. ([#161](https://github.com/harlan-zw/nuxt-seo-utils/pull/161))

```ts
import type { BreadcrumbItemProps, ShareLinksOptions } from '#seo-utils/app'
import { useBreadcrumbItems, useFallbackTitle, useShareLinks } from '#seo-utils/app'
```

The composables stay auto-imported. You need the explicit import only in files that skip auto-imports.

## ⚡ Performance

- **Sharp is optional.** Only the `nuxt-seo-utils icons` CLI uses Sharp, so the module no longer installs it. A clean Linux x64 install has 571 unique packages instead of 575. ([#168](https://github.com/harlan-zw/nuxt-seo-utils/pull/168))
- **Fewer allocations for derived tags.** The module now syncs derived `og:image`, `twitter:image`, and `og:url` tags without a temporary array per tag. In a local 31-tag helper benchmark, median CPU fell from 2.08 to 1.09 µs per call. Sampled allocation fell from 3,042 to 890 bytes per call. These figures measure the helper alone. They exclude the rest of the SSR request. ([#169](https://github.com/harlan-zw/nuxt-seo-utils/pull/169))

## 🐛 Bug Fixes

- **Disabled Nuxt [Schema.org](http://Schema.org):** v8 read `schemaOrg.enable`, so it missed `schemaOrg: { enabled: false }`. v9 reads `schemaOrg.enabled` and loads the no-op [Schema.org](http://Schema.org) fallback. ([#161](https://github.com/harlan-zw/nuxt-seo-utils/pull/161))
- **Regex corruption:** the handwritten JavaScript minifier could break a regex with an escaped slash. v9 removes that minifier. ([#168](https://github.com/harlan-zw/nuxt-seo-utils/pull/168))

## ⚠️ Breaking Changes

### Nuxt 4.6, Node 22.22.3, and Unhead 3.4.2

v9 requires Nuxt `^4.6.0 || ^5.0.0`. v8 accepted Nuxt 3.16 or later. ([#161](https://github.com/harlan-zw/nuxt-seo-utils/pull/161))

v9 also requires Node `^22.22.3 || ^24.15.0 || >=26.0.0` and Unhead `^3.4.2`. Nuxt 4.6 already depends on Unhead `^3.4.2`.

```json [package.json]
// Before
{ "devDependencies": { "nuxt": "^3.16.0" } }

// After
{ "devDependencies": { "nuxt": "^4.6.0" } }
```

### Unhead v2 Removed

v8 supported Unhead v2 and Unhead v3. On Unhead v2, `treeShakeUseSeoMeta` logged a warning and skipped the transform. v9 removes the Unhead v2 code path and its warning. ([#161](https://github.com/harlan-zw/nuxt-seo-utils/pull/161))

If your lockfile still resolves `@unhead/vue` 2, upgrade Nuxt to 4.6. Then remove any `@unhead/vue` override or pin.

### Site Config v5

v9 requires `nuxt-site-config` 5.0.0 or later. v8 accepted version 3.2 or later. ([#161](https://github.com/harlan-zw/nuxt-seo-utils/pull/161))

Nuxt now loads Site Config as a native module dependency. ([#173](https://github.com/harlan-zw/nuxt-seo-utils/pull/173)) If your `package.json` pins `nuxt-site-config`, upgrade it.

```json [package.json]
// Before
{ "dependencies": { "nuxt-site-config": "^3.2.0" } }

// After
{ "dependencies": { "nuxt-site-config": "^5.0.0" } }
```

### Sharp Is an Optional Peer

v8 installed Sharp as an optional dependency. v9 lists `sharp@^0.35.5` as an optional peer, so your package manager does not install it. ([#168](https://github.com/harlan-zw/nuxt-seo-utils/pull/168))

If you use the `nuxt-seo-utils icons` CLI, install Sharp yourself:

```bash
pnpm add -D sharp
```

### Runtime Minification Keeps Dynamic JavaScript

v8 minified inline JavaScript during SSR and in prerendered HTML with a handwritten minifier. v9 leaves that JavaScript unchanged. ([#168](https://github.com/harlan-zw/nuxt-seo-utils/pull/168))

- Static `app.head` scripts still get parser-based minification at build time.
- JSON scripts and inline CSS still get minified.
- The `minify` option keeps its shape and its default, `{ build: true, runtime: false }`.

If you need smaller inline JavaScript, move it into `app.head` or into a bundled file.

### `#seo-utils` Alias Narrowed

v8 mapped `#seo-utils` to the whole runtime directory. v9 maps only `#seo-utils/app`. ([#161](https://github.com/harlan-zw/nuxt-seo-utils/pull/161))

Deep paths under `#seo-utils/app/` still resolve. Imports from `#seo-utils/types`, `#seo-utils/server/`, and `#seo-utils/shared/` fail.

```ts
// Before
import type { BreadcrumbItemProps } from '#seo-utils/app/composables/useBreadcrumbItems'

// After
import type { BreadcrumbItemProps } from '#seo-utils/app'
```

### DevTools Host Package

The SEO Utils DevTools panel now loads through the optional `nuxtseo-devtools-host` package. Without it, the tab shows an install button. ([#161](https://github.com/harlan-zw/nuxt-seo-utils/pull/161))

```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 builds do not need it.

## Migration Steps

1. Upgrade Node to 22.22.3+, 24.15.0+, or 26+.
2. Upgrade Nuxt to 4.6 or later: `npx nuxt upgrade --force`.
3. If you pin `nuxt-site-config`, change the range to `^5.0.0`.
4. If you use the `nuxt-seo-utils icons` CLI, run `pnpm add -D sharp`.
5. Replace imports from `#seo-utils/types`, `#seo-utils/server/`, or `#seo-utils/shared/`. Import public composables and types from `#seo-utils/app`.
6. If you use the DevTools panel, run `pnpm add -D nuxtseo-devtools-host@^1`.
7. Run `nuxt dev` and read the client rendering and head warnings.

If you use `@nuxtjs/seo`, upgrade to [Nuxt SEO v6](/docs/nuxt-seo/releases/v6) instead. It installs Nuxt SEO Utils v9 for you.

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
