nuxt-seo-utils 8.6.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -15
- package/dist/cli.mjs +1 -1
- package/dist/module.d.mts +55 -3
- package/dist/module.d.ts +55 -3
- package/dist/module.json +2 -2
- package/dist/module.mjs +521 -31
- package/dist/runtime/app/composables/polyfills.d.ts +2 -2
- package/dist/runtime/app/composables/polyfills.js +2 -2
- package/dist/runtime/app/composables/schemaOrg.d.ts +2 -0
- package/dist/runtime/app/composables/schemaOrg.js +2 -0
- package/dist/runtime/app/composables/useBreadcrumbItems.js +3 -2
- package/dist/runtime/app/composables/useFallbackTitle.js +0 -8
- package/dist/runtime/app/index.d.ts +5 -0
- package/dist/runtime/app/index.js +3 -0
- package/dist/runtime/app/logic/applyDefaults.js +1 -2
- package/dist/runtime/app/logic/syncDerivedTags.js +25 -15
- package/dist/runtime/app/plugins/0.routeRules.d.ts +1 -1
- package/dist/runtime/app/plugins/0.routeRules.js +1 -1
- package/dist/runtime/app/plugins/1.absoluteImageUrls.server.d.ts +1 -1
- package/dist/runtime/app/plugins/1.absoluteImageUrls.server.js +1 -2
- package/dist/runtime/app/plugins/colorModeIcons.d.ts +1 -1
- package/dist/runtime/app/plugins/colorModeIcons.js +1 -1
- package/dist/runtime/app/plugins/defaults.d.ts +1 -1
- package/dist/runtime/app/plugins/defaultsWaitI18n.d.ts +1 -1
- package/dist/runtime/app/plugins/derivedTags.d.ts +1 -1
- package/dist/runtime/app/plugins/derivedTags.js +1 -2
- package/dist/runtime/app/plugins/inferSeoMetaPlugin.d.ts +1 -1
- package/dist/runtime/app/plugins/inferSeoMetaPlugin.js +2 -16
- package/dist/runtime/app/plugins/minifyScripts.server.d.ts +1 -1
- package/dist/runtime/app/plugins/minifyScripts.server.js +2 -11
- package/dist/runtime/app/plugins/siteConfig.d.ts +1 -1
- package/dist/runtime/app/plugins/siteConfig.js +1 -2
- package/dist/runtime/app/plugins/titles.d.ts +1 -1
- package/dist/runtime/app/plugins/titles.js +1 -2
- package/dist/runtime/app/plugins/titlesWaitI18n.d.ts +1 -1
- package/dist/runtime/app/plugins/titlesWaitI18n.js +1 -2
- package/dist/runtime/server/middleware/redirectCanonical.d.ts +1 -1
- package/dist/runtime/server/middleware/redirectCanonical.js +6 -6
- package/dist/runtime/server/middleware/redirects.d.ts +2 -0
- package/dist/runtime/server/middleware/redirects.js +21 -0
- package/dist/runtime/server/middleware/resolveImagesInPagesDir.d.ts +1 -1
- package/dist/runtime/server/middleware/resolveImagesInPagesDir.js +5 -6
- package/dist/runtime/server/plugins/minifyHtml.js +2 -9
- package/dist/runtime/server/routes/__nuxt-seo-utils/debug.d.ts +27 -1
- package/dist/runtime/server/routes/__nuxt-seo-utils/debug.js +2 -3
- package/dist/runtime/shared/minify.d.ts +0 -6
- package/dist/runtime/shared/minify.js +0 -60
- package/dist/runtime/shared/redirects.d.ts +40 -0
- package/dist/runtime/shared/redirects.js +59 -0
- package/dist/runtime/types.d.ts +15 -0
- package/dist/types.d.mts +1 -1
- package/package.json +42 -37
- package/skills/nuxt-seo-utils/SKILL.md +52 -6
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { joinURL, withoutTrailingSlash } from "ufo";
|
|
2
|
+
export const REDIRECT_CACHE_CONTROL = "public, max-age=3600";
|
|
3
|
+
export function normalizePath(path) {
|
|
4
|
+
return path === "/" ? path : withoutTrailingSlash(path);
|
|
5
|
+
}
|
|
6
|
+
export function isAbsoluteUrl(to) {
|
|
7
|
+
return /^https?:\/\//i.test(to);
|
|
8
|
+
}
|
|
9
|
+
export function parseSourcePattern(source) {
|
|
10
|
+
if (!source.startsWith("/") || source.startsWith("//"))
|
|
11
|
+
return "it must be a path that starts with `/`";
|
|
12
|
+
if (/[?#]/.test(source))
|
|
13
|
+
return "route matching ignores the query string and hash, so remove them";
|
|
14
|
+
const wildcard = source.endsWith("/**");
|
|
15
|
+
const base = wildcard ? source.slice(0, -3) : source;
|
|
16
|
+
if (/\*|\/:/.test(base))
|
|
17
|
+
return "only a trailing `/**` is supported";
|
|
18
|
+
if (wildcard) {
|
|
19
|
+
const prefix = normalizePath(base || "/");
|
|
20
|
+
return { _tag: "Prefix", prefix: prefix === "/" ? "" : prefix };
|
|
21
|
+
}
|
|
22
|
+
return { _tag: "Exact", path: normalizePath(base) };
|
|
23
|
+
}
|
|
24
|
+
export function matchesSource(pattern, path) {
|
|
25
|
+
const normalized = normalizePath(path);
|
|
26
|
+
if (pattern._tag === "Exact")
|
|
27
|
+
return pattern.path === normalized;
|
|
28
|
+
return normalized === (pattern.prefix || "/") || normalized.startsWith(`${pattern.prefix}/`);
|
|
29
|
+
}
|
|
30
|
+
export function createRedirectMatcher(entries) {
|
|
31
|
+
const exact = /* @__PURE__ */ new Map();
|
|
32
|
+
const prefixes = [];
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
if (entry.pattern._tag === "Exact")
|
|
35
|
+
exact.set(entry.pattern.path, entry);
|
|
36
|
+
else
|
|
37
|
+
prefixes.push(entry);
|
|
38
|
+
}
|
|
39
|
+
const prefixLength = (entry) => entry.pattern._tag === "Prefix" ? entry.pattern.prefix.length : 0;
|
|
40
|
+
prefixes.sort((a, b) => prefixLength(b) - prefixLength(a));
|
|
41
|
+
return (path) => {
|
|
42
|
+
const normalized = normalizePath(path);
|
|
43
|
+
return exact.get(normalized) ?? prefixes.find((entry) => matchesSource(entry.pattern, normalized));
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function resolveTarget(rule, path) {
|
|
47
|
+
if (!rule.to.endsWith("/**"))
|
|
48
|
+
return rule.to;
|
|
49
|
+
const rest = rule.pattern._tag === "Prefix" ? normalizePath(path).slice(rule.pattern.prefix.length) : "";
|
|
50
|
+
return joinURL(rule.to.slice(0, -3) || "/", rest);
|
|
51
|
+
}
|
|
52
|
+
export function appendSearch(target, search) {
|
|
53
|
+
if (!search || search === "?")
|
|
54
|
+
return target;
|
|
55
|
+
const hashIndex = target.indexOf("#");
|
|
56
|
+
const base = hashIndex === -1 ? target : target.slice(0, hashIndex);
|
|
57
|
+
const hash = hashIndex === -1 ? "" : target.slice(hashIndex);
|
|
58
|
+
return `${base}${base.includes("?") ? "&" : "?"}${search.replace(/^\?/, "")}${hash}`;
|
|
59
|
+
}
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
|
+
import type { UseHeadOptions } from '@unhead/vue';
|
|
1
2
|
import type { Head, Link, MetaFlat, RawInput } from '@unhead/vue/types';
|
|
3
|
+
export interface SeoUtilsRuntimeConfig {
|
|
4
|
+
canonicalQueryWhitelist: string[];
|
|
5
|
+
canonicalLowercase: boolean;
|
|
6
|
+
automaticTwitterTags: boolean;
|
|
7
|
+
tagPriority: UseHeadOptions['tagPriority'];
|
|
8
|
+
separator?: string;
|
|
9
|
+
titleSeparator?: string;
|
|
10
|
+
colorModeIcons?: ColorModeIconLinks;
|
|
11
|
+
}
|
|
12
|
+
declare module 'nuxt/schema' {
|
|
13
|
+
interface PublicRuntimeConfig {
|
|
14
|
+
'seo-utils': SeoUtilsRuntimeConfig;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
2
17
|
export interface ColorModeIconLinks {
|
|
3
18
|
dark: Link[];
|
|
4
19
|
light: Link[];
|
package/dist/types.d.mts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-seo-utils",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "9.0.0",
|
|
5
5
|
"description": "SEO utilities to improve your Nuxt sites discoverability and shareability",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"funding": "https://github.com/sponsors/harlan-zw",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
],
|
|
31
31
|
"build": {
|
|
32
32
|
"externals": [
|
|
33
|
+
"nuxt/server",
|
|
33
34
|
"unhead",
|
|
34
35
|
"@unhead/vue",
|
|
35
36
|
"@unhead/vue/plugins",
|
|
@@ -37,13 +38,17 @@
|
|
|
37
38
|
"@unhead/vue/vite"
|
|
38
39
|
]
|
|
39
40
|
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": "^22.22.3 || ^24.15.0 || >=26.0.0"
|
|
43
|
+
},
|
|
40
44
|
"peerDependencies": {
|
|
41
|
-
"@unhead/vue": "^
|
|
45
|
+
"@unhead/vue": "^3.4.2",
|
|
42
46
|
"esbuild": ">=0.17.0",
|
|
43
47
|
"lightningcss": ">=1.20.0",
|
|
44
|
-
"nuxt": "^
|
|
48
|
+
"nuxt": "^4.6.0 || ^5.0.0",
|
|
45
49
|
"rolldown": ">=1.0.0-beta.0",
|
|
46
|
-
"
|
|
50
|
+
"sharp": "^0.35.5",
|
|
51
|
+
"unhead": "^3.4.2"
|
|
47
52
|
},
|
|
48
53
|
"peerDependenciesMeta": {
|
|
49
54
|
"@unhead/vue": {
|
|
@@ -58,59 +63,60 @@
|
|
|
58
63
|
"rolldown": {
|
|
59
64
|
"optional": true
|
|
60
65
|
},
|
|
66
|
+
"sharp": {
|
|
67
|
+
"optional": true
|
|
68
|
+
},
|
|
61
69
|
"unhead": {
|
|
62
70
|
"optional": true
|
|
63
71
|
}
|
|
64
72
|
},
|
|
65
73
|
"dependencies": {
|
|
66
|
-
"@nuxt/kit": "^4.
|
|
74
|
+
"@nuxt/kit": "^4.6.0",
|
|
67
75
|
"citty": "^0.2.2",
|
|
68
76
|
"consola": "^3.4.2",
|
|
69
77
|
"defu": "^6.1.7",
|
|
70
78
|
"escape-string-regexp": "^5.0.0",
|
|
71
|
-
"exsolve": "^1.1.
|
|
72
|
-
"nuxt-site-config": "^
|
|
73
|
-
"nuxtseo-shared": "^
|
|
79
|
+
"exsolve": "^1.1.3",
|
|
80
|
+
"nuxt-site-config": "^5.0.0",
|
|
81
|
+
"nuxtseo-shared": "^6.0.0",
|
|
74
82
|
"pathe": "^2.0.3",
|
|
75
|
-
"pkg-types": "^2.3.1",
|
|
76
83
|
"scule": "^1.3.0",
|
|
77
84
|
"tinyglobby": "^0.2.17",
|
|
78
85
|
"ufo": "^1.6.4"
|
|
79
86
|
},
|
|
80
|
-
"optionalDependencies": {
|
|
81
|
-
"sharp": "^0.35.4"
|
|
82
|
-
},
|
|
83
87
|
"devDependencies": {
|
|
84
|
-
"@antfu/eslint-config": "^9.
|
|
85
|
-
"@nuxt/devtools-kit": "4.0.0-
|
|
88
|
+
"@antfu/eslint-config": "^9.5.1",
|
|
89
|
+
"@nuxt/devtools-kit": "4.0.0-beta.4",
|
|
86
90
|
"@nuxt/module-builder": "^1.0.3",
|
|
87
|
-
"@nuxt/schema": "^4.
|
|
88
|
-
"@nuxt/test-utils": "^4.
|
|
89
|
-
"@nuxt/ui": "^4.11.
|
|
91
|
+
"@nuxt/schema": "^4.6.0",
|
|
92
|
+
"@nuxt/test-utils": "^4.3.3",
|
|
93
|
+
"@nuxt/ui": "^4.11.3",
|
|
90
94
|
"@nuxtjs/i18n": "^10.6.0",
|
|
91
|
-
"@
|
|
92
|
-
"@
|
|
93
|
-
"@vue
|
|
94
|
-
"
|
|
95
|
+
"@nuxtjs/sitemap": "^9.0.0",
|
|
96
|
+
"@types/node": "^26.6.4",
|
|
97
|
+
"@unhead/vue": "^3.4.2",
|
|
98
|
+
"@vue/test-utils": "^2.5.1",
|
|
99
|
+
"bumpp": "^12.3.0",
|
|
95
100
|
"case-police": "^2.2.1",
|
|
96
101
|
"cheerio": "^1.2.0",
|
|
97
|
-
"eslint": "^10.
|
|
98
|
-
"eslint-plugin-harlanzw": "^0.
|
|
99
|
-
"happy-dom": "^20.
|
|
100
|
-
"lint-staged": "^17.
|
|
102
|
+
"eslint": "^10.12.0",
|
|
103
|
+
"eslint-plugin-harlanzw": "^0.23.0",
|
|
104
|
+
"happy-dom": "^20.14.5",
|
|
105
|
+
"lint-staged": "^17.6.0",
|
|
101
106
|
"markdownlint-cli": "^0.49.1",
|
|
102
|
-
"nuxt": "
|
|
107
|
+
"nuxt": "4.6.0",
|
|
103
108
|
"nuxt-security": "^2.6.0",
|
|
104
|
-
"nuxtseo-layer-devtools": "^5.3.
|
|
105
|
-
"playwright-core": "^1.
|
|
106
|
-
"sass": "^1.
|
|
109
|
+
"nuxtseo-layer-devtools": "^5.3.16",
|
|
110
|
+
"playwright-core": "^1.63.0",
|
|
111
|
+
"sass": "^1.105.1",
|
|
112
|
+
"sharp": "^0.35.5",
|
|
107
113
|
"typescript": "6.0.3",
|
|
108
114
|
"ultrahtml": "^1.7.0",
|
|
109
115
|
"unbuild": "^3.6.1",
|
|
110
|
-
"unhead": "3.2
|
|
111
|
-
"vitest": "^
|
|
112
|
-
"vue": "^3.5.
|
|
113
|
-
"vue-tsc": "^3.3.
|
|
116
|
+
"unhead": "^3.4.2",
|
|
117
|
+
"vitest": "^5.0.3",
|
|
118
|
+
"vue": "^3.5.43",
|
|
119
|
+
"vue-tsc": "^3.3.12"
|
|
114
120
|
},
|
|
115
121
|
"scripts": {
|
|
116
122
|
"client:build": "node -e \"require('fs').cpSync('devtools','dist/devtools',{recursive:true})\"",
|
|
@@ -123,14 +129,13 @@
|
|
|
123
129
|
"typecheck": "nuxt typecheck",
|
|
124
130
|
"prepare:fixtures": "nuxt prepare test/fixtures/basic && nuxt prepare test/fixtures/i18n && nuxt prepare test/fixtures/csp",
|
|
125
131
|
"test": "vitest && pnpm test:nuxt5",
|
|
126
|
-
"test:run": "vitest run --exclude '**/\\_\\_runtime\\_\\_/**'",
|
|
132
|
+
"test:run": "vitest run --exclude '**/\\_\\_runtime\\_\\_/**' && pnpm test:nuxt5",
|
|
127
133
|
"test:runtime": "pnpm test:runtime:basic && pnpm test:runtime:i18n",
|
|
128
134
|
"test:runtime:basic": "cd test/fixtures/basic && vitest --run",
|
|
129
135
|
"test:runtime:i18n": "cd test/fixtures/i18n && vitest --run",
|
|
130
|
-
"test:nuxt5": "pnpm
|
|
136
|
+
"test:nuxt5": "pnpm build && pnpm --dir test/fixtures/nuxt5 install --frozen-lockfile && pnpm --dir test/fixtures/nuxt5 test",
|
|
131
137
|
"release": "pnpm build && bumpp -x \"npx changelogen --output=CHANGELOG.md\"",
|
|
132
138
|
"lint:docs": "markdownlint ./docs/content && case-police 'docs/content/**/*.md' *.md",
|
|
133
|
-
"lint:docs:fix": "markdownlint ./docs/content --fix && case-police 'docs/content/**/*.md' *.md --fix"
|
|
134
|
-
"test:compat-v2": "node test/compat-v2/run.mjs"
|
|
139
|
+
"lint:docs:fix": "markdownlint ./docs/content --fix && case-police 'docs/content/**/*.md' *.md --fix"
|
|
135
140
|
}
|
|
136
141
|
}
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: nuxt-seo-utils
|
|
3
|
-
description: Set up, override, and debug the default SEO head tags in a Nuxt app with the nuxt-seo-utils module. Use when a task mentions canonical URLs, the title template or separator, fallback titles, og:image or twitter:image files, favicons and app icons, seo.meta, seoMeta or head in routeRules, useBreadcrumbItems, useShareLinks, the nuxt-seo-utils icons CLI, or the seo config key. Also use when a page tag does not override a default, html lang ignores useHead, or og:image renders twice.
|
|
3
|
+
description: Set up, override, and debug the default SEO head tags in a Nuxt app with the nuxt-seo-utils module. Use when a task mentions canonical URLs, the title template or separator, fallback titles, og:image or twitter:image files, favicons and app icons, seo.meta, seoMeta or head in routeRules, useBreadcrumbItems, useShareLinks, the nuxt-seo-utils icons CLI, seo.redirects, moved or deleted URLs, 301 or 410 responses, or the seo config key. Also use when a page tag does not override a default, html lang ignores useHead, or og:image renders twice.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: "Requires Node.js ^22.22.3 || ^24.15.0 || >=26.0.0. Requires Nuxt ^4.6.0 || ^5.0.0. Requires Unhead ^3.4.2."
|
|
4
6
|
---
|
|
5
7
|
|
|
6
8
|
# nuxt-seo-utils
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
Requires Nuxt `^4.6.0 || ^5.0.0` and Unhead 3.4.2 or newer.
|
|
9
11
|
The module adds default head tags from site config, reads icon and social image files, and ships breadcrumb and share link composables.
|
|
10
12
|
Docs: https://nuxtseo.com/docs/seo-utils
|
|
11
13
|
|
|
12
14
|
## Setup
|
|
13
15
|
|
|
16
|
+
Install with `npx nuxt module add nuxt-seo-utils`.
|
|
17
|
+
Included in `@nuxtjs/seo` v6. If the app uses `@nuxtjs/seo`, configure the `seo` key and do not add the module again.
|
|
14
18
|
The config key is `seo`. `seoUtils` does nothing.
|
|
15
19
|
Set `site.url` and `site.name`. The canonical URL, `og:url`, the title template, and absolute image URLs read them.
|
|
16
20
|
|
|
@@ -66,7 +70,7 @@ Generate icon files from one source image in `public/`:
|
|
|
66
70
|
pnpm exec nuxt-seo-utils icons --source logo.svg
|
|
67
71
|
```
|
|
68
72
|
|
|
69
|
-
It writes `favicon.ico`, `apple-touch-icon.png`, and `icon-{16,32,192,512}` PNGs to `public/`.
|
|
73
|
+
It writes `favicon.ico`, `apple-touch-icon.png`, and `icon-{16,32,192,512}` PNGs to `public/`. Install the optional peer with `pnpm add -D sharp` before generating icons.
|
|
70
74
|
|
|
71
75
|
## Site-wide and per-route meta
|
|
72
76
|
|
|
@@ -115,25 +119,66 @@ const share = useShareLinks({ title: post.value.title, utm: { source: 'auto', ca
|
|
|
115
119
|
`title` defaults to `site.name`, not the page title. Pass it.
|
|
116
120
|
UTM defaults to `utm_source=<platform>` and `utm_medium=social` (or `email`). An object without `source: 'auto'` switches to manual mode: `{ campaign: 'launch' }` sends only `utm_campaign`.
|
|
117
121
|
|
|
122
|
+
## Redirects and gone URLs
|
|
123
|
+
|
|
124
|
+
`seo.redirects` lists old URLs, keyed by path without `app.baseURL`. Every entry needs `addedAt`.
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
export default defineNuxtConfig({
|
|
128
|
+
seo: {
|
|
129
|
+
redirects: {
|
|
130
|
+
'/blog/old-post': { to: '/blog/new-post', addedAt: '2026-10-13' }, // 301; set statusCode: 308 to keep the method
|
|
131
|
+
'/docs/v1/**': { to: '/docs/**', addedAt: '2026-10-13' }, // /docs/v1/setup -> /docs/setup
|
|
132
|
+
'/spring-sale': { statusCode: 410, addedAt: '2026-10-13', reason: 'Sale ended' }, // error page with 410
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
})
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
- A moved URL has `to` (a path or an absolute URL) and `statusCode` 301 (default) or 308. A gone URL has no `to` and `statusCode` 410 or 404.
|
|
139
|
+
- The request query string is kept. A trailing slash does not change the match. An exact key beats a `/**` key.
|
|
140
|
+
- `reason` is a private note. Visitors never see it.
|
|
141
|
+
- Nothing expires. After `redirectReviewAfterDays` (365) from `addedAt` or `reviewedAt`, `nuxt dev` and `nuxt build` list the entry for review. To keep it, set `reviewedAt` to today. Delete it only when the old URL gets no visits and no backlinks.
|
|
142
|
+
- Chains flatten at build: `/a` to `/b` and `/b` to `/c` serve `/a` to `/c`. A loop or an invalid entry stops the build. A redirect to a gone URL warns.
|
|
143
|
+
- With an i18n prefix strategy, each unprefixed entry also applies under each locale prefix. A translated path needs its own entry.
|
|
144
|
+
- For a long list, import a file typed with `satisfies Redirects` (`import type { Redirects } from 'nuxt-seo-utils'`).
|
|
145
|
+
- Do not redirect deleted pages to the home page. Google can treat that as a soft 404. Use a gone URL.
|
|
146
|
+
- Use `routeRules` `redirect` for temporary 302 or 307 redirects. `seo.redirects` accepts only permanent ones.
|
|
147
|
+
|
|
118
148
|
## Traps
|
|
119
149
|
|
|
120
150
|
- **`htmlAttrs.lang` in `useHead()` or `app.head` is ignored.** The module sets `lang` from site config. Set `site.defaultLocale`, or use `@nuxtjs/i18n`.
|
|
121
151
|
- **Before nuxt-schema-org 6.4.1, the Schema.org WebPage URL keeps the route case.** `canonicalLowercase` lowercases the canonical, `og:url`, `useShareLinks()` URLs, and `BreadcrumbList` item URLs. nuxt-schema-org before 6.4.1 builds the WebPage `url` from the route path. There, `/blog/My-Post` gets a canonical of `/blog/my-post` and a WebPage URL of `/blog/My-Post`. From 6.4.1, the WebPage `url` follows the canonical. On an older version, upgrade nuxt-schema-org, use lowercase route paths, or set `canonicalLowercase: false`. Breadcrumb `to` links keep their case on purpose.
|
|
122
152
|
- **`definePageMeta({ title })` takes a static string only.** Use `useSeoMeta({ title })` for data.
|
|
153
|
+
- **Only some static presets get native redirects.** `nuxt generate` writes host rules for `netlify-static` (`_redirects`, 308 becomes 301), `vercel-static` (`config.json` routes), and `cloudflare-pages-static` (`_redirects`, no 410: gone URLs get the host 404 page). With `netlify-static` and an `app.baseURL`, and with every other preset, each exact moved URL becomes an instant meta refresh page, and gone URLs and `/**` keys need host rules. The build lists each entry the host cannot serve.
|
|
154
|
+
- **A catch-all page skips client-side redirects.** `seo.redirects` runs on the server. A `NuxtLink` to an old URL that a `[...slug].vue` page matches renders that page. Fix the link. Nuxt Link Checker flags a link to a 301 entry.
|
|
123
155
|
|
|
124
156
|
## Version limits
|
|
125
157
|
|
|
158
|
+
v9 breaking changes and their fixes:
|
|
159
|
+
|
|
160
|
+
- Nuxt `^4.6.0 || ^5.0.0`, Node `^22.22.3 || ^24.15.0 || >=26.0.0`, and Unhead `^3.4.2` are required. Upgrade Nuxt and Node first.
|
|
161
|
+
- Unhead v2 is not supported. Remove any `@unhead/vue` 2 pin or override.
|
|
162
|
+
- `nuxt-site-config` 5 is required. Change a pinned `nuxt-site-config` to `^5.0.0`.
|
|
163
|
+
- The module no longer installs Sharp. Run `pnpm add -D sharp` before `nuxt-seo-utils icons`.
|
|
164
|
+
- Runtime and prerender minification keep inline JavaScript unchanged. Put scripts that need minification in `app.head`.
|
|
165
|
+
- Only `#seo-utils/app` resolves. Replace `#seo-utils/types`, `#seo-utils/server/`, and `#seo-utils/shared/` imports with `#seo-utils/app` exports.
|
|
166
|
+
- The DevTools panel needs `nuxtseo-devtools-host` as a dev dependency.
|
|
167
|
+
|
|
168
|
+
Older limits:
|
|
169
|
+
|
|
126
170
|
- 8.5.2 and earlier: a page `ogImage` keeps the default image's dimensions and `twitter:image`, a page canonical leaves `og:url` on the route URL, a `pages/<route>/og-image.png` renders `og:image:alt` twice, and `public/opengraph-image.*` is ignored.
|
|
127
171
|
- 8.5.2 and earlier: `BreadcrumbList` item URLs keep the route case, and `useShareLinks()` lowercases without the site locale.
|
|
128
172
|
- 8.5.2 and earlier: `seo.meta` and `public/og-image.png` beat route rule `seoMeta` and `pages/**/og-image.png`. Put the route value in the page with `useSeoMeta()`.
|
|
129
|
-
- `treeShakeUseSeoMeta` needs Unhead v3. On Unhead v2 the module skips it with a warning.
|
|
130
173
|
- `automaticTwitterTags: false` (removes `twitter:card`) arrived in 8.5.0.
|
|
131
174
|
|
|
132
175
|
## Config
|
|
133
176
|
|
|
134
177
|
- `redirectToCanonicalSiteUrl` (`false`): in production, send a 301 from other hosts to the `site.url` host.
|
|
135
178
|
- `canonicalQueryWhitelist`: replaces the default list. It does not extend it.
|
|
136
|
-
- `
|
|
179
|
+
- `redirects`: moved and gone URLs. See the section above.
|
|
180
|
+
- `redirectReviewAfterDays` (`365`): days until an entry is due for review. `false` turns the review off.
|
|
181
|
+
- `minify` (`{ build: true, runtime: false }`): minify static `app.head` JavaScript/CSS with parser-based tooling. Compact JSON/CSS in prerendered HTML. `runtime: true` compacts JSON/CSS per SSR request. Dynamic JavaScript and custom script types remain unchanged.
|
|
137
182
|
- `automaticOgAndTwitterTags` (`true`): the `og:title`, `og:description`, and `twitter:card` inference.
|
|
138
183
|
- Other options: https://nuxtseo.com/docs/seo-utils/api/config
|
|
139
184
|
|
|
@@ -141,4 +186,5 @@ UTM defaults to `utm_source=<platform>` and `utm_medium=social` (or `email`). An
|
|
|
141
186
|
|
|
142
187
|
- On `nuxt dev` and `nuxt build`, the module warns about conflicts in `app.head`, such as a static `lang` or an `og:site_name` that differs from `site.name`. Fix the site config, not the head. Set `validateAppHead: false` to hide the check.
|
|
143
188
|
- `/__nuxt-seo-utils/debug.json` in dev, or in production with `seo: { debug: true }`.
|
|
144
|
-
-
|
|
189
|
+
- On `nuxt dev` and `nuxt build`, the module warns when SSR is off for indexable pages. Turn SSR on for public routes. For private routes, set `robots: false` with `@nuxtjs/robots`.
|
|
190
|
+
- Nuxt DevTools has an SEO Utils tab. The panel needs `nuxtseo-devtools-host` as a dev dependency.
|