
Nuxt Seo
- 247 installs
- 202 repo stars
- Updated August 4, 2026
- secondsky/claude-skills
Use nuxt-seo for development tasks
About
nuxt-seo: A skill for development. This provides functionality for development workflows.
- nuxt-seo
Nuxt Seo by the numbers
- 247 all-time installs (skills.sh)
- +12 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,558 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/secondsky/claude-skills --skill nuxt-seoAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 247 |
|---|---|
| repo stars | ★ 202 |
| Last updated | August 4, 2026 |
| Repository | secondsky/claude-skills ↗ |
What it does
Use nuxt-seo for development tasks
Files
Nuxt SEO v5
Status: Production Ready | Dependencies: Nuxt >=3.0.0
Use this skill when building SEO-optimized Nuxt applications with any combination of the 8 official Nuxt SEO modules plus standalone modules.
---
Quick Start (5 Minutes)
1. Install Complete SEO Bundle
# Recommended (v5)
npx nuxt module add @nuxtjs/seo2. Configure Site Settings
export default defineNuxtConfig({
modules: ['@nuxtjs/seo'],
site: {
url: 'https://example.com',
name: 'My Awesome Site',
description: 'Building amazing web experiences',
defaultLocale: 'en'
}
})CRITICAL (v5 Breaking Change):
site.nameis NO LONGER auto-inferred frompackage.json— set it explicitly- Set
site.urlto production URL (required for sitemaps and canonical URLs) - Set
defaultLocaleif using i18n
3. Restart and Verify
Visit these URLs to verify:
/robots.txt- Robots file/sitemap.xml- Sitemap/__robots__/debug-production.json- Debug (v5)/__sitemap__/debug-production.json- Debug (v5)
Individual Module Install
npx nuxt module add @nuxtjs/robots
npx nuxt module add @nuxtjs/sitemap
npx nuxt module add nuxt-og-image---
Module Overview
| Module | Version | Purpose |
|---|---|---|
| @nuxtjs/seo | v5.1.0 | Primary SEO module (installs all 8 as bundle) |
| @nuxtjs/robots | v6.0.6 | Manages robots.txt and bot detection |
| @nuxtjs/sitemap | v8.0.11 | Generates XML sitemaps with advanced features |
| nuxt-og-image | v6.3.1 | Creates Open Graph images via Vue templates |
| nuxt-schema-org | v6.0.4 | Builds Schema.org structured data graphs |
| nuxt-link-checker | v5.0.6 | Finds and fixes broken links with ESLint integration |
| nuxt-seo-utils | v8.1.4 | SEO utilities, share links, favicons, inline minification |
| nuxt-site-config | v4.0.7 | Centralized site configuration management |
Standalone (MIT): nuxt-ai-ready (llms.txt), nuxt-skew-protection (version safety) Pro (Paid): Nuxt SEO Pro — Search Console, Core Web Vitals, MCP server
For detailed module docs: Load references/module-details.md
---
Critical Rules
Always Do
- Set
site.urlANDsite.nameexplicitly in nuxt.config.ts (v5: name no longer auto-inferred) - Use environment variables for multi-environment setups
- Configure robots.txt to block admin/private pages
- Add Schema.org structured data to all important pages
- Generate OG images for social sharing
- Use
getSiteConfig(event)on server side (v5:useSiteConfig(event)removed) - Use
defineSitemapSchema()for Content v3 (v5:asSitemapCollection()deprecated)
Never Do
- Forget to set
site.urlandsite.name(breaks sitemaps, canonical URLs, titles) - Allow crawling of staging environments
- Use
useSiteConfig(event)on server side (v5: usegetSiteConfig(event)) - Use
asSitemapCollection()(v5: usedefineSitemapSchema()) - Use
getSiteIndexable()(v5: use{ indexable } = getSiteConfig(event))
---
Known Issues Prevention
Issue #1: Sitemap Not Generating
Error: /sitemap.xml returns 404 | Fix: Set site.url in nuxt.config.ts
Issue #2: robots.txt Missing
Error: /robots.txt not accessible | Fix: Install @nuxtjs/robots and set site.url
Issue #3: OG Images Not Rendering
Error: /__og-image__/og.png returns error | Fix: Use Satori-compatible CSS or switch to Chromium renderer
Issue #4: Schema Validation Errors
Error: Invalid JSON-LD | Fix: Follow official Schema.org types, validate with Google Rich Results Test
Issue #5: Broken Internal Links
Error: 404 on internal links | Fix: Enable nuxt-link-checker with ESLint rules during development
Issue #6: Duplicate Meta Tags
Error: Multiple meta tags with same property | Fix: Let modules handle meta tags automatically
Issue #7: Canonical URL Issues
Error: Wrong canonical URL | Fix: Configure site.url and trailingSlash correctly
Issue #8: Sitemap Index Errors
Error: Sitemap index XML malformed | Fix: Use chunkSize option to split large sitemaps
Issue #9: Crawling Staging Environment
Error: Staging indexed by Google | Fix: disallow: process.env.NUXT_PUBLIC_ENV === 'staging' ? ['/'] : []
Issue #10: Missing Social Sharing Images
Error: No preview on social media | Fix: Use defineOgImage() on all important pages
Issue #11: Missing Site Name (v5 Breaking)
Error: Site title/og:site_name missing | Fix: Set site.name in nuxt.config.ts or NUXT_SITE_NAME env var Ref: https://nuxtseo.com/docs/nuxt-seo/migration-guide/v4-to-v5
Issue #12: Server-Side useSiteConfig Error (v5 Breaking)
Error: useSiteConfig is not a function on server | Fix: Use getSiteConfig(event) on server side Ref: https://github.com/harlan-zw/nuxt-site-config/releases
Issue #13: Deprecated Content Composables (v5 Breaking)
Error: asSitemapCollection is not defined | Fix: Use defineSitemapSchema(), defineSchemaOrgSchema(), defineRobotsSchema() Ref: https://nuxtseo.com/docs/nuxt-seo/migration-guide/v4-to-v5
Issue #14: OG Image Security Errors (v5)
Error: OG image requests return 403 or signature errors | Fix: Use defineOgImage() properly; don't manually construct OG image URLs Ref: https://nuxtseo.com/docs/og-image/getting-started/introduction
---
Configuration Example
export default defineNuxtConfig({
modules: ['@nuxtjs/seo'],
site: {
url: process.env.NUXT_PUBLIC_SITE_URL,
name: 'My Site',
defaultLocale: 'en'
},
robots: {
disallow: process.env.NUXT_PUBLIC_ENV === 'staging' ? ['/'] : []
},
sitemap: {
sitemaps: {
blog: { sources: ['/api/__sitemap__/blog'] }
}
}
})New v5 Features
- Social Share Links:
useShareLinks({ title, twitter, utm })with UTM tracking - Favicon Generation:
npx nuxt-seo-utils icons --source logo.svg - ESLint Link Checking:
import linkChecker from 'nuxt-link-checker/eslint' - definePageMeta Sitemap:
definePageMeta({ sitemap: { changefreq: 'daily', priority: 0.8 } }) - Inline Minification:
seo: { minify: true }
---
When to Load References
Load reference files based on the user's specific needs:
| Load When | Reference File |
|---|---|
| Upgrading v4→v5, breaking changes | references/v5-migration-guide.md |
| Rendering modes, JSON-LD, canonical URLs, IndexNow | references/seo-guides.md |
| AI optimization, llms.txt, MCP tools | references/pro-modules.md |
| I18n SEO, route rules, link checker rules | references/advanced-seo-guides.md |
| OG image templates, Satori/Chromium, fonts | references/og-image-guide.md |
| Nuxt Content integration, asSeoCollection | references/nuxt-content-integration.md |
| Dynamic sitemaps, multi-sitemaps, chunking | references/sitemap-advanced.md |
| Server-side hooks, Nitro plugins | references/nitro-api-reference.md |
| llms.txt, AI crawlers, content signals | references/ai-seo-tools.md |
| Module capabilities overview | references/modules-overview.md |
| First-time setup, package manager specifics | references/installation-guide.md |
| Composable API docs, parameter lists | references/api-reference.md |
| Blog, e-commerce, multi-language patterns | references/common-patterns.md |
| Specific module configuration | references/module-details.md |
| Production SEO guidelines | references/best-practices.md |
| Error resolution, module conflicts | references/troubleshooting.md |
| Multi-environment, advanced features | references/advanced-configuration.md |
Bundled Resources
Agents
| Agent | Purpose |
|---|---|
seo-auditor.md | Comprehensive SEO audit |
schema-generator.md | Generate Schema.org structured data |
og-image-generator.md | Create custom OG image templates |
link-checker.md | Analyze internal/external links |
sitemap-builder.md | Design optimal sitemap strategies |
Commands
| Command | Purpose |
|---|---|
/seo-audit | Run comprehensive SEO audit |
/seo-setup | Quick Nuxt SEO project setup |
/og-preview | Preview OG image generation |
/check-links | Run link checker analysis |
/validate-sitemap | Validate sitemap configuration |
/check-schema | Validate Schema.org implementation |
Assets
assets/package-versions.json— Current module versions for verification
---
Package Versions (Verified 2026-04-02)
{
"dependencies": {
"@nuxtjs/seo": "^5.1.0",
"@nuxtjs/robots": "^6.0.6",
"@nuxtjs/sitemap": "^8.0.11",
"nuxt-og-image": "^6.3.1",
"nuxt-schema-org": "^6.0.4",
"nuxt-link-checker": "^5.0.6",
"nuxt-seo-utils": "^8.1.4",
"nuxt-site-config": "^4.0.7"
}
}---
Official Documentation
- Nuxt SEO: https://nuxtseo.com
- v5 Migration: https://nuxtseo.com/docs/nuxt-seo/migration-guide/v4-to-v5
- @nuxtjs/robots: https://nuxtseo.com/docs/robots/getting-started/introduction
- @nuxtjs/sitemap: https://nuxtseo.com/docs/sitemap/getting-started/introduction
- nuxt-og-image: https://nuxtseo.com/docs/og-image/getting-started/introduction
- nuxt-schema-org: https://nuxtseo.com/docs/schema-org/getting-started/introduction
- nuxt-link-checker: https://nuxtseo.com/docs/link-checker/getting-started/introduction
- GitHub: https://github.com/harlan-zw
Production Ready: All patterns based on official documentation from https://nuxtseo.com/llms-full.txt | Last verified: 2026-04-02
{
"verified_date": "2026-03-30",
"nuxt_seo_version": "v5",
"modules": {
"@nuxtjs/seo": {
"version": "5.1.0",
"downloads": "4.2M/month",
"stars": 3300,
"npm": "https://www.npmjs.com/package/@nuxtjs/seo",
"github": "https://github.com/harlan-zw/nuxt-seo",
"docs": "https://nuxtseo.com/docs/nuxt-seo/getting-started/introduction"
},
"@nuxtjs/robots": {
"version": "6.0.6",
"downloads": "8.7M",
"npm": "https://www.npmjs.com/package/@nuxtjs/robots",
"github": "https://github.com/nuxt-modules/robots",
"docs": "https://nuxtseo.com/docs/robots/getting-started/introduction"
},
"@nuxtjs/sitemap": {
"version": "8.0.11",
"downloads": "10M",
"npm": "https://www.npmjs.com/package/@nuxtjs/sitemap",
"github": "https://github.com/nuxt-modules/sitemap",
"docs": "https://nuxtseo.com/docs/sitemap/getting-started/introduction"
},
"nuxt-og-image": {
"version": "6.3.1",
"downloads": "3.7M",
"npm": "https://www.npmjs.com/package/nuxt-og-image",
"github": "https://github.com/nuxt-modules/og-image",
"docs": "https://nuxtseo.com/docs/og-image/getting-started/introduction"
},
"nuxt-schema-org": {
"version": "6.0.4",
"downloads": "3.9M",
"npm": "https://www.npmjs.com/package/nuxt-schema-org",
"github": "https://github.com/harlan-zw/nuxt-schema-org",
"docs": "https://nuxtseo.com/docs/schema-org/getting-started/introduction"
},
"nuxt-link-checker": {
"version": "5.0.6",
"downloads": "2.8M",
"npm": "https://www.npmjs.com/package/nuxt-link-checker",
"github": "https://github.com/harlan-zw/nuxt-link-checker",
"docs": "https://nuxtseo.com/docs/link-checker/getting-started/introduction"
},
"nuxt-seo-utils": {
"version": "8.1.4",
"downloads": "2.2M",
"npm": "https://www.npmjs.com/package/nuxt-seo-utils",
"github": "https://github.com/harlan-zw/nuxt-seo-utils",
"docs": "https://nuxtseo.com/docs/seo-utils/getting-started/introduction"
},
"nuxt-site-config": {
"version": "4.0.7",
"downloads": "7.9M",
"npm": "https://www.npmjs.com/package/nuxt-site-config",
"github": "https://github.com/harlan-zw/nuxt-site-config",
"docs": "https://nuxtseo.com/docs/site-config/getting-started/introduction"
}
},
"pro_modules": {
"nuxt-ai-ready": {
"version": "1.1.0",
"license": "MIT",
"description": "Generate llms.txt and llms-full.txt for AI crawlers. Now MIT licensed and standalone.",
"docs": "https://nuxtseo.com/docs/ai-ready/getting-started/introduction"
},
"nuxt-skew-protection": {
"version": "1.1.0",
"license": "MIT",
"description": "Prevent version mismatches during deployments. Now MIT licensed and standalone.",
"docs": "https://nuxtseo.com/docs/skew-protection/getting-started/introduction"
},
"nuxt-seo-pro": {
"description": "SEO Pro dashboard with Search Console integration, Core Web Vitals, and MCP server ($119 one-time)",
"docs": "https://nuxtseo.com/pro"
}
},
"version_bump_summary": {
"_comment": "Module versions changed from Nuxt SEO v4 to v5",
"nuxt-site-config": "v3 -> v4",
"nuxt-seo-utils": "v7 -> v8",
"@nuxtjs/sitemap": "v7 -> v8",
"@nuxtjs/robots": "v5 -> v6",
"nuxt-schema-org": "v5 -> v6",
"nuxt-link-checker": "v4 -> v5",
"nuxt-og-image": "v6 -> v6 (no major change)"
},
"requirements": {
"nuxt": ">=3.0.0",
"node": ">=18.0.0"
},
"source": "https://nuxtseo.com/llms-full.txt",
"maintainer": "Harlan Wilton",
"license": "MIT"
}
Nuxt SEO Advanced Configuration
Production-ready configuration examples for all Nuxt SEO modules
---
Table of Contents
1. Complete nuxt.config.ts Example 2. Multi-Environment Setup 3. Production Configuration 4. Development Configuration 5. Staging Configuration
---
Complete nuxt.config.ts Example
Production-ready configuration with all 8 modules configured:
export default defineNuxtConfig({
modules: ['@nuxtjs/seo'],
site: {
url: process.env.NUXT_PUBLIC_SITE_URL || 'https://example.com',
name: 'My Awesome Site',
description: 'Building amazing web experiences',
defaultLocale: 'en'
},
robots: {
disallow: process.env.NUXT_PUBLIC_ENV === 'staging' ? ['/'] : ['/admin', '/private'],
sitemap: `${process.env.NUXT_PUBLIC_SITE_URL}/sitemap.xml`,
cleanParam: ['utm_source', 'utm_medium', 'utm_campaign']
},
sitemap: {
strictNuxtContentPaths: true,
sitemaps: {
pages: {
includeAppSources: true
},
blog: {
sources: ['/api/__sitemap__/blog']
},
products: {
sources: ['/api/__sitemap__/products']
}
},
defaults: {
changefreq: 'daily',
priority: 0.7
},
exclude: ['/admin/**', '/private/**']
},
ogImage: {
renderer: 'satori',
format: 'png',
fonts: [
'Inter:400',
'Inter:700'
]
},
schemaOrg: {
identity: {
type: 'Organization',
name: 'My Company',
url: process.env.NUXT_PUBLIC_SITE_URL,
logo: `${process.env.NUXT_PUBLIC_SITE_URL}/logo.png`,
sameAs: [
'https://twitter.com/mycompany',
'https://facebook.com/mycompany',
'https://linkedin.com/company/mycompany'
]
}
},
linkChecker: {
enabled: process.env.NODE_ENV === 'development',
showLiveInspections: true,
failOnError: false,
excludeLinks: ['/temp/*']
},
routeRules: {
'/': { sitemap: { changefreq: 'daily', priority: 1.0 } },
'/about': { sitemap: { changefreq: 'monthly', priority: 0.8 } },
'/blog/**': { sitemap: { changefreq: 'weekly', priority: 0.9 } },
'/admin/**': { sitemap: { exclude: true }, robots: 'noindex, nofollow' }
}
})Why These Settings
- Environment-based configuration for multi-environment setups
- Blocks staging from search engines (critical for SEO)
- Separate sitemaps for different content types (blog, products, pages)
- Automatic robots.txt generation with sitemap reference
- OG image optimization with Satori renderer (fast, no runtime)
- Link checking in development only (prevents production overhead)
- Route-specific SEO rules for fine-grained control
---
Multi-Environment Setup
Environment Variables
Create .env files for each environment:
.env (local development):
NUXT_PUBLIC_SITE_URL=http://localhost:3000
NUXT_PUBLIC_SITE_NAME=My Site (Dev)
NUXT_PUBLIC_ENV=development.env.staging:
NUXT_PUBLIC_SITE_URL=https://staging.example.com
NUXT_PUBLIC_SITE_NAME=My Site (Staging)
NUXT_PUBLIC_ENV=staging.env.production:
NUXT_PUBLIC_SITE_URL=https://example.com
NUXT_PUBLIC_SITE_NAME=My Site
NUXT_PUBLIC_ENV=productionDynamic Configuration Based on Environment
// nuxt.config.ts
const isProduction = process.env.NUXT_PUBLIC_ENV === 'production'
const isStaging = process.env.NUXT_PUBLIC_ENV === 'staging'
const isDevelopment = process.env.NODE_ENV === 'development'
export default defineNuxtConfig({
modules: ['@nuxtjs/seo'],
site: {
url: process.env.NUXT_PUBLIC_SITE_URL,
name: process.env.NUXT_PUBLIC_SITE_NAME
},
robots: {
// Block ALL crawling on staging
disallow: isStaging ? ['/'] : ['/admin', '/private']
},
sitemap: {
// Enable sitemap on production only
enabled: isProduction,
sitemaps: isProduction ? {
pages: { includeAppSources: true },
blog: { sources: ['/api/__sitemap__/blog'] }
} : undefined
},
linkChecker: {
// Enable link checking in development only
enabled: isDevelopment,
failOnError: false
},
ogImage: {
// Use Chromium in development for full CSS support
renderer: isDevelopment ? 'chromium' : 'satori'
}
})---
Production Configuration
Optimized for performance and SEO:
export default defineNuxtConfig({
modules: ['@nuxtjs/seo'],
site: {
url: 'https://example.com',
name: 'My Awesome Site',
description: 'Building amazing web experiences',
defaultLocale: 'en',
trailingSlash: false
},
robots: {
disallow: ['/admin', '/private', '/api'],
sitemap: 'https://example.com/sitemap.xml',
cleanParam: ['utm_source', 'utm_medium', 'utm_campaign', 'ref']
},
sitemap: {
strictNuxtContentPaths: true,
sitemaps: {
pages: { includeAppSources: true },
blog: { sources: ['/api/__sitemap__/blog'] },
products: { sources: ['/api/__sitemap__/products'] }
},
defaults: {
changefreq: 'daily',
priority: 0.7
},
exclude: ['/admin/**', '/private/**', '/api/**'],
// For large sites: enable chunking
chunksSize: 1000
},
ogImage: {
renderer: 'satori', // Fast, no runtime
format: 'png',
quality: 90,
fonts: ['Inter:400', 'Inter:700']
},
schemaOrg: {
identity: {
type: 'Organization',
name: 'My Company',
url: 'https://example.com',
logo: 'https://example.com/logo.png',
sameAs: [
'https://twitter.com/mycompany',
'https://facebook.com/mycompany'
]
}
},
linkChecker: {
enabled: false // Disable in production
},
routeRules: {
'/': { sitemap: { changefreq: 'daily', priority: 1.0 } },
'/blog/**': { sitemap: { changefreq: 'weekly', priority: 0.9 } },
'/admin/**': { sitemap: { exclude: true }, robots: 'noindex, nofollow' }
}
})---
Development Configuration
Optimized for debugging and testing:
export default defineNuxtConfig({
modules: ['@nuxtjs/seo'],
site: {
url: 'http://localhost:3000',
name: 'My Site (Dev)'
},
robots: {
disallow: ['/'] // Block all in development
},
sitemap: {
enabled: true,
debug: true // Enable debug logging
},
ogImage: {
renderer: 'chromium', // Full CSS support for testing
debug: true
},
schemaOrg: {
identity: {
type: 'Organization',
name: 'My Company (Dev)'
}
},
linkChecker: {
enabled: true,
showLiveInspections: true,
failOnError: false, // Warn but don't fail
skipInspections: [] // Check all links
}
})---
Staging Configuration
Prevent search engine indexing while testing:
export default defineNuxtConfig({
modules: ['@nuxtjs/seo'],
site: {
url: 'https://staging.example.com',
name: 'My Site (Staging)'
},
robots: {
disallow: ['/'], // CRITICAL: Block all crawling
sitemap: undefined // Don't expose sitemap on staging
},
sitemap: {
enabled: false // Disable sitemap generation
},
ogImage: {
renderer: 'satori',
fonts: ['Inter:400', 'Inter:700']
},
schemaOrg: {
identity: {
type: 'Organization',
name: 'My Company (Staging)'
}
},
linkChecker: {
enabled: true,
failOnError: true // Catch broken links before production
}
})---
Advanced Patterns
Multi-Language Configuration
site: {
url: 'https://example.com',
name: 'My Site',
defaultLocale: 'en',
locales: ['en', 'es', 'fr']
},
sitemap: {
i18n: {
locales: ['en', 'es', 'fr'],
defaultLocale: 'en'
}
}Custom Sitemap for Dynamic Routes
// server/api/__sitemap__/blog.ts
export default defineSitemapEventHandler(async () => {
const posts = await fetchAllPosts()
return posts.map(post => ({
loc: `/blog/${post.slug}`,
lastmod: post.updatedAt,
changefreq: 'weekly',
priority: 0.9,
images: post.images?.map(img => ({
loc: img.url,
caption: img.alt
})),
// Multi-language support
alternatives: post.translations?.map(t => ({
href: `/blog/${t.slug}`,
hreflang: t.locale
}))
}))
})Route-Specific OG Images
routeRules: {
'/': {
ogImage: {
component: 'HomeOgImage'
}
},
'/blog/**': {
ogImage: {
component: 'BlogOgImage'
}
},
'/products/**': {
ogImage: {
component: 'ProductOgImage'
}
}
}---
Deployment Checklist
Before deploying to production:
- [ ]
NUXT_PUBLIC_SITE_URLset to production URL - [ ]
NUXT_PUBLIC_ENVset toproduction - [ ] Staging robots.txt blocks all crawling
- [ ] Sitemap enabled for production only
- [ ] Link checker disabled in production
- [ ] OG image renderer set to
satorifor performance - [ ] Schema.org identity configured
- [ ] Test
/sitemap.xmlendpoint - [ ] Test
/robots.txtendpoint - [ ] Verify OG images render correctly
- [ ] Submit sitemap to Google Search Console
---
Last Updated: 2025-11-27
Nuxt SEO v5 - Advanced SEO Guides
Advanced SEO topics including I18n, Route Rules, Link Checking, Enhanced Titles, SPA Prerendering, Hydration, Crawler Protection, and 404 Pages.
v5 Note: Several server-side APIs changed in v5. See ./v5-migration-guide.md for details.- UsegetSiteConfig(event)instead ofuseSiteConfig(event)on the server
- site.name must be explicitly set (no longer auto-inferred)- Content v3 composables renamed:defineSitemapSchema,defineSchemaOrgSchema,defineRobotsSchema
---
Table of Contents
1. I18n Multilanguage SEO 2. SEO Route Rules 3. Enhanced Titles 4. Nuxt SEO Utils Deep Dive 5. Nuxt Link Checker Rules 6. Prerendering Vue SPA for SEO 7. Hydration Mismatches 8. Protecting from Malicious Crawlers 9. SEO-Friendly 404 Pages
---
I18n Multilanguage SEO
Overview
Both nuxt-robots and nuxt-sitemap integrate seamlessly with @nuxtjs/i18n and nuxt-i18n-micro for multilanguage SEO.
Robots.txt with I18n
The robots module automatically localizes allow and disallow paths based on your i18n configuration.
// nuxt.config.ts
export default defineNuxtConfig({
robots: {
disallow: ['/secret', '/admin'],
},
i18n: {
locales: ['en', 'fr', 'de'],
defaultLocale: 'en',
strategy: 'prefix',
}
})Generated robots.txt:
User-agent: *
Disallow: /en/secret
Disallow: /en/admin
Disallow: /fr/secret
Disallow: /fr/admin
Disallow: /de/secret
Disallow: /de/adminOpt-out of I18n Localization
// Per-group opt-out
export default defineNuxtConfig({
robots: {
groups: [
{
disallow: ['/docs/en/v*', '/docs/zh/v*'],
_skipI18n: true, // Skip i18n for this group
},
],
},
})
// Global opt-out
export default defineNuxtConfig({
robots: {
autoI18n: false, // Disable all i18n localization
}
})Sitemap with I18n
The sitemap module automatically generates locale-specific sitemaps:
Generated structure:
/sitemap_index.xml
/en-sitemap.xml
/fr-sitemap.xml
/de-sitemap.xmlAutomatic Multi-Sitemap Mode
Enabled automatically when:
- Not using
no_prefixstrategy - Or using Different Domains
- And
sitemapsoption not manually configured
Dynamic URLs with I18n Transform
// server/api/__sitemap__/urls.ts
export default defineSitemapEventHandler(() => {
return [
{
loc: '/about-us',
// Automatically creates: /en/about-us, /fr/about-us, /de/about-us
_i18nTransform: true,
}
]
})Custom Path Translations
// nuxt.config.ts
export default defineNuxtConfig({
i18n: {
pages: {
'about': {
en: '/about',
fr: '/a-propos',
de: '/uber-uns',
},
},
},
})With _i18nTransform: true, this generates:
/about(en)/fr/a-propos(fr)/de/uber-uns(de)
Assign URL to Specific Locale
// server/api/__sitemap__/urls.ts
export default defineSitemapEventHandler(() => {
return [
{
loc: '/about-us',
_sitemap: 'en', // Only appears in English sitemap
}
]
})Debugging Hreflang
Display hreflang counts in sitemap UI:
export default defineNuxtConfig({
sitemap: {
xslColumns: [
{ label: 'URL', width: '50%' },
{ label: 'Last Modified', select: 'sitemap:lastmod', width: '25%' },
{ label: 'Hreflangs', select: 'count(xhtml)', width: '25%' },
],
}
})---
SEO Route Rules
Overview
Nuxt's routeRules provide powerful per-route SEO configuration for robots, sitemap, rendering mode, and meta tags.
Basic Route Rules
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
// Homepage - highest priority, daily updates
'/': {
sitemap: { changefreq: 'daily', priority: 1.0 },
prerender: true,
},
// Blog posts - weekly updates, prerendered
'/blog/**': {
sitemap: { changefreq: 'weekly', priority: 0.8 },
prerender: true,
},
// Admin area - no indexing, no sitemap
'/admin/**': {
robots: 'noindex, nofollow',
sitemap: { exclude: true },
},
// Search results - don't index (prevents duplicate content)
'/search': {
robots: 'noindex, follow',
},
// User profiles - no index (privacy)
'/user/**': {
robots: 'noindex',
sitemap: { exclude: true },
},
// API routes - exclude from everything
'/api/**': {
robots: 'noindex',
sitemap: { exclude: true },
},
// Dynamic products with ISR
'/products/**': {
swr: 3600, // Revalidate every hour
sitemap: { changefreq: 'daily', priority: 0.7 },
},
// Static pages
'/about': { prerender: true },
'/contact': { prerender: true },
'/privacy': { prerender: true },
}
})SEO Meta in Route Rules
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
'/products/**': {
seoMeta: {
ogType: 'product',
twitterCard: 'summary_large_image',
},
},
'/blog/**': {
seoMeta: {
ogType: 'article',
twitterCard: 'summary_large_image',
},
},
}
})Environment-Based Route Rules
// nuxt.config.ts
const isProduction = process.env.NODE_ENV === 'production'
const isStaging = process.env.NUXT_PUBLIC_ENV === 'staging'
export default defineNuxtConfig({
routeRules: {
// Block all crawling on staging
...(isStaging && {
'/**': { robots: 'noindex, nofollow' }
}),
// Production-only prerendering
...(isProduction && {
'/': { prerender: true },
'/blog/**': { prerender: true },
}),
}
})---
Enhanced Titles
Overview
Nuxt SEO Utils provides enhanced title management with fallback titles and templates.
Automatic Fallback Titles
Every page automatically gets a title generated from the last URL segment:
/about-us → "About Us"
/products/blue-shoes → "Blue Shoes"
/blog/my-first-post → "My First Post"Title Templates
// nuxt.config.ts
export default defineNuxtConfig({
site: {
name: 'My Site',
},
})This automatically creates title template: %s | My Site
Custom Title Templates
<script setup>
// Override for specific page
useHead({
titleTemplate: '%s - Custom Suffix'
})
// Or remove template entirely
useHead({
titleTemplate: null
})
</script>Page-Level Title Configuration
<script setup>
// Simple title
useSeoMeta({
title: 'My Page Title',
})
// Title with description
useSeoMeta({
title: 'Product Name',
description: 'Product description for search results',
ogTitle: 'Product Name - Special Offer', // Different for social
})
</script>Title Best Practices
1. Keep titles under 60 characters - Google truncates longer titles 2. Put keywords near the beginning - More visible in search results 3. Make titles unique per page - Avoid duplicate titles 4. Include brand name - Usually at the end with separator 5. Be descriptive - Tell users what the page is about
---
Nuxt SEO Utils Deep Dive
Features Overview
| Feature | Description |
|---|---|
| Default Canonical URLs | Automatic canonical URL generation |
| Metadata Files | Next.js-style metadata file support |
| Breadcrumb Composable | Easy breadcrumb generation |
| SEO Meta in Config | useSeoMeta DX in nuxt.config |
| Automatic OG Tags | og:title/og:description from page meta |
| Tag Validation | Fix broken tags automatically |
| Head Optimizations | Treeshaking and Capo.js |
Canonical URLs
// nuxt.config.ts
export default defineNuxtConfig({
seoUtils: {
// Whitelist specific query params for canonical
canonicalQueryParams: ['page', 'sort'],
}
})Features:
- Automatic lowercase URLs
- Respects trailing slash config
- Query param whitelisting
Breadcrumbs
<script setup>
const breadcrumbs = useBreadcrumbItems()
// Returns: [
// { label: 'Home', to: '/' },
// { label: 'Products', to: '/products' },
// { label: 'Shoes', to: '/products/shoes' },
// ]
</script>
<template>
<!-- Works directly with Nuxt UI -->
<UBreadcrumb :items="breadcrumbs" />
<!-- Or custom rendering -->
<nav aria-label="Breadcrumb">
<ol class="flex gap-2">
<li v-for="(item, i) in breadcrumbs" :key="i">
<NuxtLink :to="item.to">{{ item.label }}</NuxtLink>
<span v-if="i < breadcrumbs.length - 1">/</span>
</li>
</ol>
</nav>
</template>Metadata Files (Next.js-style)
Place files in public/ directory:
public/
├── favicon.ico
├── apple-touch-icon.png
├── og-image.png
└── opengraph-image.pngAuto-detected and added to <head>.
Automatic OG Tag Inference
<script setup>
// Just set title and description
useSeoMeta({
title: 'My Page',
description: 'Page description',
})
// og:title and og:description are automatically set
// No need to duplicate!
</script>---
Nuxt Link Checker Rules
Available Inspection Rules
| Rule | Description | Why It Matters |
|---|---|---|
absolute-site-urls | Checks for internal absolute links | Use relative paths for portability |
link-text | Ensures descriptive link text | Accessibility and SEO |
missing-hash | Validates anchor links | UX and accessibility |
no-baseless | Checks for document-relative links | Maintenance issues |
no-double-slashes | Finds // in paths | Canonicalization issues |
no-duplicate-query-params | Finds ?a=1&a=2 | Caching/duplicate content |
no-error-response | Finds 4xx/5xx responses | Crawlability |
no-javascript | Finds href="javascript:" | Poor UX |
no-missing-href | Ensures <a> has href | Accessibility |
no-non-ascii-chars | Finds non-ASCII in URLs | Encoding issues |
no-underscores | Finds _ in URLs | SEO best practice |
no-uppercase-chars | Finds uppercase in URLs | SEO best practice |
no-whitespace | Finds spaces in URLs | Broken links |
trailing-slash | Consistent trailing slashes | Canonicalization |
Disabling Specific Rules
// nuxt.config.ts
export default defineNuxtConfig({
linkChecker: {
skipInspections: [
'no-underscores', // Allow underscores
'absolute-site-urls', // Allow absolute internal URLs
'link-text', // Skip link text validation
],
},
})Using Link Checker DevTools
1. Open Nuxt DevTools in development 2. Navigate to "Link Checker" tab 3. See live inspections as you browse 4. Click issues to jump to source
Build-Time Link Checking
Link checker runs during nuxt build for all prerendered pages. Prerender pages you want checked:
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
prerender: {
routes: ['/', '/about', '/blog'],
crawlLinks: true,
}
}
})---
Prerendering Vue SPA for SEO
The SPA SEO Problem
Single Page Applications (SPAs) render content client-side, meaning:
- Search engines see empty HTML
- Social media previews don't work
- Core Web Vitals suffer
Solutions in Nuxt
Option 1: SSR (Default)
// nuxt.config.ts
export default defineNuxtConfig({
ssr: true, // Default, renders on server
})Best for: Dynamic content, personalization, real-time data
Option 2: SSG (Static Generation)
bun run generate
# or: npx nuxt generate// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
prerender: {
routes: ['/'],
crawlLinks: true, // Auto-discover links
}
}
})Best for: Content sites, blogs, documentation
Option 3: Hybrid (Mixed Rendering)
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
// Static pages
'/': { prerender: true },
'/about': { prerender: true },
'/blog/**': { prerender: true },
// SSR pages
'/dashboard/**': { ssr: true },
// SPA pages (no SEO needed)
'/admin/**': { ssr: false },
// ISR pages
'/products/**': { swr: 3600 },
}
})Best for: Complex sites with mixed content types
Option 4: ISR (Incremental Static Regeneration)
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
'/products/**': {
swr: 3600, // Stale-while-revalidate: 1 hour
},
'/blog/**': {
isr: 600, // Regenerate every 10 minutes
},
}
})Best for: High-traffic sites with frequently updated content
---
Hydration Mismatches
What Are Hydration Mismatches?
Hydration is when Vue "takes over" server-rendered HTML. Mismatches occur when server HTML differs from client expectation.
Common Causes
1. Date/Time rendering - Server and client in different timezones 2. Random values - Different on server vs client 3. Browser APIs - window, document not available on server 4. Async data - Data changes between render and hydration
Solutions
1. Use <ClientOnly> for Browser-Only Content
<template>
<ClientOnly>
<CurrentTime />
<template #fallback>
<span>Loading...</span>
</template>
</ClientOnly>
</template>2. Use onMounted for Client-Side Code
<script setup>
const windowWidth = ref(0)
onMounted(() => {
windowWidth.value = window.innerWidth
})
</script>3. Consistent Date Formatting
<script setup>
// Bad - different on server/client
const date = new Date().toLocaleString()
// Good - consistent ISO format
const date = new Date().toISOString()
// Or use a library like dayjs with consistent timezone
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
dayjs.extend(utc)
const date = dayjs().utc().format('YYYY-MM-DD HH:mm:ss')
</script>4. Avoid Random Values in Renders
<script setup>
// Bad - different every render
const id = Math.random().toString(36)
// Good - stable ID
const id = useId()
</script>SEO Impact of Hydration Mismatches
- Console warnings indicate potential issues
- Content shifts hurt Core Web Vitals (CLS)
- Inconsistent content can confuse crawlers
---
Protecting from Malicious Crawlers
Bot Detection with nuxt-robots
// nuxt.config.ts
export default defineNuxtConfig({
robots: {
groups: [
// Block AI scrapers
{
userAgent: ['GPTBot', 'ChatGPT-User', 'CCBot', 'anthropic-ai'],
disallow: ['/'],
},
// Block known bad bots
{
userAgent: ['AhrefsBot', 'SemrushBot', 'MJ12bot'],
disallow: ['/'],
},
// Allow search engines
{
userAgent: ['Googlebot', 'Bingbot', 'DuckDuckBot'],
allow: ['/'],
},
],
}
})Server-Side Bot Detection
// server/middleware/bot-protection.ts
export default defineEventHandler((event) => {
const { isBot, name } = getBotDetection(event)
if (isBot) {
// Log bot access
console.log(`Bot detected: ${name}`)
// Rate limit bots
// Serve cached content
// Block specific bots
}
})Client-Side Bot Detection
<script setup>
const { isBot, name } = useBotDetection()
if (isBot) {
// Don't load analytics
// Don't show interactive features
// Serve simplified content
}
</script>Blocking by User-Agent
// server/middleware/block-bots.ts
const blockedBots = ['BadBot', 'EvilCrawler', 'SpamBot']
export default defineEventHandler((event) => {
const userAgent = getHeader(event, 'user-agent') || ''
for (const bot of blockedBots) {
if (userAgent.includes(bot)) {
throw createError({
statusCode: 403,
message: 'Access denied',
})
}
}
})Rate Limiting
// server/middleware/rate-limit.ts
const rateLimits = new Map<string, { count: number; timestamp: number }>()
const WINDOW_MS = 60000 // 1 minute
const MAX_REQUESTS = 60
export default defineEventHandler((event) => {
const ip = getRequestIP(event) || 'unknown'
const now = Date.now()
const record = rateLimits.get(ip)
if (!record || now - record.timestamp > WINDOW_MS) {
rateLimits.set(ip, { count: 1, timestamp: now })
return
}
record.count++
if (record.count > MAX_REQUESTS) {
throw createError({
statusCode: 429,
message: 'Too many requests',
})
}
})---
SEO-Friendly 404 Pages
Creating a Custom 404 Page
<!-- error.vue -->
<script setup>
const error = useError()
// Set proper status code
useHead({
title: 'Page Not Found',
})
useSeoMeta({
title: 'Page Not Found',
description: 'The page you are looking for does not exist.',
robots: 'noindex, follow', // Don't index 404 pages
})
</script>
<template>
<div class="error-page">
<h1>{{ error.statusCode === 404 ? 'Page Not Found' : 'Error' }}</h1>
<p>{{ error.message }}</p>
<!-- Helpful navigation -->
<nav>
<h2>Try these instead:</h2>
<ul>
<li><NuxtLink to="/">Home</NuxtLink></li>
<li><NuxtLink to="/search">Search</NuxtLink></li>
<li><NuxtLink to="/sitemap">Sitemap</NuxtLink></li>
</ul>
</nav>
<button @click="clearError({ redirect: '/' })">
Go to Homepage
</button>
</div>
</template>404 Page Best Practices
1. Return 404 status code - Don't soft 404 (200 with "not found" content) 2. Use `noindex` - Prevent 404 pages from being indexed 3. Provide helpful navigation - Help users find what they need 4. Include search - Let users search for content 5. Match site design - Consistent branding and navigation 6. Track 404s - Monitor for broken links to fix
Handling Soft 404s
A "soft 404" returns 200 status but shows "not found" content. This confuses search engines.
// pages/products/[id].vue
const { data: product, error } = await useFetch(`/api/products/${id}`)
if (!product.value) {
throw createError({
statusCode: 404,
message: 'Product not found',
})
}OG Image for 404 Pages
<!-- error.vue -->
<script setup>
defineOgImage({
component: 'Error',
title: 'Page Not Found',
description: 'The requested page could not be found',
})
</script>Monitoring 404 Errors
// plugins/error-tracking.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('vue:error', (error, instance, info) => {
// Send to error tracking service
console.error('Vue error:', error)
})
nuxtApp.hook('app:error', (error) => {
// Track 404s and other errors
if (error.statusCode === 404) {
// Log or send to analytics
console.log('404 error:', error.url)
}
})
})---
Last Updated: 2025-12-28 Source: https://nuxtseo.com
Nuxt AI Ready - Complete Guide
Make your Nuxt site discoverable by AI agents through llms.txt, MCP servers, and markdown APIs.
Note: Nuxt AI Ready is a Nuxt SEO Pro module requiring a license.
---
Table of Contents
1. Overview 2. Installation 3. llms.txt Generation 4. On-Demand Markdown 5. Model Context Protocol (MCP) 6. Content Signals 7. Hooks Reference 8. Configuration
---
Overview
Users increasingly ask AI assistants questions your site could answer—but LLMs only cite sources they can parse. Two standards are emerging to solve this:
- [llms.txt](https://llmstxt.org/) — AI-readable site summaries
- [MCP](https://modelcontextprotocol.io/) — Protocol for agents to query your content directly
Features
- llms.txt Generation: Automatic
/llms.txtand/llms-full.txtfiles at build time - On-Demand Markdown: Any route available as
.md(e.g.,/about→/about.md) - MCP Server: Tools for AI agent integration (
list_pages,search_pages_fuzzy) - Content Signals: Configure AI training/search permissions via robots.txt
---
Installation
# Install the module
bunx nuxi module add nuxt-ai-ready
# Requires sitemap module
bunx nuxi module add @nuxtjs/sitemap// nuxt.config.ts
export default defineNuxtConfig({
modules: [
'@nuxtjs/sitemap',
'nuxt-ai-ready'
],
site: {
url: 'https://example.com'
}
})---
llms.txt Generation
/llms.txt
Site overview with page links. Built from page metadata collected during prerender.
Format:
# <Site Title>
> <Site Description>
## Pages
- [Page Title](/page-link): Meta Description
...
## <Section Title>
- [Link Title](/link): Description
...
<Notes>Live example: nuxtseo.com/llms.txt
/llms-full.txt
Full markdown content for all pages. Streamed during prerender—each page appended as processed.
Configuration
// nuxt.config.ts
export default defineNuxtConfig({
aiReady: {
llmsTxt: {
sections: [
{
title: 'API Reference',
links: [
{ title: 'REST API', href: '/docs/api', description: 'API documentation' }
]
}
],
notes: 'Built with Nuxt AI Ready'
}
}
})Modify with Hook
// nuxt.config.ts
export default defineNuxtConfig({
hooks: {
'ai-ready:llms-txt': (payload) => {
payload.sections.push({
title: 'Custom APIs',
links: [{ title: 'Search', href: '/api/search', description: 'Search endpoint' }]
})
payload.notes.push('Custom note')
}
}
})Page Discovery Flow
Phase 1 (Prerender) Phase 2 (Sitemap) Runtime
───────────────────── ───────────────────── ─────────────────────
app:rendered sitemap:prerender:done GET /llms.txt
↓ ↓ ↓
Queue .md routes Parse sitemap.xml fetchSitemapUrls()
↓ ↓ ↓
HTML → Markdown Fetch .md for SSR Combine prerendered
↓ pages + sitemap URLs
Write JSONL + ↓ ↓
llms-full.txt Add to JSONL only Generate llms.txt---
On-Demand Markdown
Any route can be accessed as .md for AI consumption.
Example: /about → /about.md
Customizing Conversion
// server/plugins/mdream.ts
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('ai-ready:mdreamConfig', (config) => {
// Skip navigation elements
config.ignoreSelectors = ['nav', '.sidebar', '.footer']
// Preserve code blocks
config.preserveCodeBlocks = true
})
})Post-Process Markdown
// server/plugins/markdown.ts
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('ai-ready:markdown', (ctx) => {
// Add source link
ctx.markdown += `\n\n---\nSource: ${ctx.route}`
})
})---
Model Context Protocol (MCP)
Connect AI agents like Claude to your Nuxt site.
Installation
npx nuxi module add @nuxtjs/mcp-toolkit// nuxt.config.ts
export default defineNuxtConfig({
modules: [
'nuxt-ai-ready',
'@nuxtjs/mcp-toolkit',
],
})Connect Claude Desktop
Add to Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"my-site": {
"command": "npx",
"args": ["-y", "@nuxtjs/mcp-client", "https://example.com/mcp"]
}
}
}Available Tools
list_pages
Returns page metadata as JSON. Cached 1 hour.
Response:
[
{
"route": "/docs/getting-started",
"title": "Getting Started",
"description": "Quick start guide",
"headings": "h1:Getting Started|h2:Installation",
"updatedAt": "2025-01-15T10:30:00Z"
}
]search_pages_fuzzy
Fuzzy search across pages via Fuse.js. Searches title, description, and route. Cached 5 minutes.
Parameters:
| Param | Type | Description |
|---|---|---|
query | string | Search query |
limit | number | Max results (default: 10) |
Response:
[
{
"route": "/docs/installation",
"title": "Installation",
"description": "Install the module",
"score": 0.15
}
]Available Resources
resource://nuxt-ai-ready/pages
Page listing as JSON. Same data as list_pages tool. Cached 1 hour.
Configuration
// nuxt.config.ts
export default defineNuxtConfig({
aiReady: {
mcp: {
tools: false, // Disable all tools
resources: false, // Disable all resources
},
}
})---
Content Signals
Control how AI systems interact with your content through robots.txt directives.
Standards
Content-Usage (values: y/n)
train-ai— foundation model training
Content-Signal (values: yes/no)
search— indexing/snippetsai-input— RAG, grounding, AI searchai-train— model training/fine-tuning
Enable Content Signals
Disabled by default. Enable to allow AI training and indexing:
// nuxt.config.ts
export default defineNuxtConfig({
aiReady: {
contentSignal: {
aiTrain: true,
search: true,
aiInput: true
}
}
})Produces in robots.txt:
# Nuxt AI Ready Content Signals
Content-Usage: train-ai=y
Content-Signal: ai-train=yes, search=yes, ai-input=yesSelective Permissions
Allow search indexing but block training:
// nuxt.config.ts
export default defineNuxtConfig({
aiReady: {
contentSignal: {
search: true,
aiInput: false,
aiTrain: false
}
}
})Set contentSignal: false to disable entirely (default).
---
Hooks Reference
Nuxt Hooks (Build-time)
ai-ready:llms-txt
Modify llms.txt sections before generation.
export default defineNuxtConfig({
hooks: {
'ai-ready:llms-txt': (payload) => {
payload.sections.push({
title: 'Custom Section',
links: [/* ... */]
})
payload.notes.push('Custom note')
}
}
})ai-ready:page:markdown
Modify or filter pages during prerender.
export default defineNuxtConfig({
hooks: {
'ai-ready:page:markdown': (ctx) => {
// Skip draft pages
if (ctx.route.startsWith('/drafts/')) {
ctx.markdown = '' // Empty = excluded
return
}
// Add frontmatter
ctx.markdown = `---
route: ${ctx.route}
title: ${ctx.title}
---
${ctx.markdown}`
}
}
})Context properties:
route: Page path (e.g.,/about)markdown: Converted content (mutable)title: Extracted<title>description: Extracted meta descriptionheadings: Array of{ level, text }objects
Nitro Hooks (Runtime)
ai-ready:mdreamConfig
Customize HTML → markdown conversion.
// server/plugins/mdream.ts
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('ai-ready:mdreamConfig', (config) => {
config.ignoreSelectors = ['nav', '.sidebar', '.footer']
config.preserveCodeBlocks = true
})
})ai-ready:markdown
Post-process markdown at runtime for .md requests.
// server/plugins/markdown.ts
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('ai-ready:markdown', (ctx) => {
ctx.markdown += `\n\n---\nSource: ${ctx.route}`
})
})---
Configuration
Full Configuration Reference
// nuxt.config.ts
export default defineNuxtConfig({
aiReady: {
// llms.txt configuration
llmsTxt: {
enabled: true,
sections: [
{
title: 'Section Title',
links: [
{ title: 'Link', href: '/path', description: 'Description' }
]
}
],
notes: 'Footer notes'
},
// Content signals for robots.txt
contentSignal: {
aiTrain: false, // Allow AI training
search: false, // Allow search indexing
aiInput: false // Allow RAG/grounding
},
// MCP configuration
mcp: {
tools: true, // Enable MCP tools
resources: true // Enable MCP resources
},
// Markdown conversion
markdown: {
enabled: true // Enable .md routes
}
}
})Environment-Based Configuration
// nuxt.config.ts
export default defineNuxtConfig({
aiReady: {
// Only enable in production
contentSignal: process.env.NODE_ENV === 'production'
? { aiTrain: true, search: true, aiInput: true }
: false
}
})---
Sitemap Requirements
The module requires @nuxtjs/sitemap for page discovery:
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/sitemap', 'nuxt-ai-ready']
})Excluding Pages
Pages excluded from sitemap are automatically excluded from llms.txt:
// nuxt.config.ts
export default defineNuxtConfig({
sitemap: {
exclude: ['/admin/**', '/api/**', '/drafts/**']
}
})---
Dev Mode Notes
In development:
- llms.txt returns a notice about missing data
- Page data only available after
nuxi generateornuxi build --prerender - Runtime
.mdroutes still work for testing markdown conversion
For full functionality, test with a production build.
---
Official Documentation
- Nuxt AI Ready: https://nuxtseo.com/docs/ai-ready/getting-started/introduction
- llms.txt Standard: https://llmstxt.org/
- Model Context Protocol: https://modelcontextprotocol.io/
- Content Signals: https://contentsignals.org/
- GitHub: https://github.com/nuxt-seo-pro/nuxt-ai-ready
Nuxt SEO - Complete API Reference
All composables, functions, and APIs across the 8 modules.
---
Table of Contents
1. nuxt-robots APIs 2. nuxt-sitemap APIs 3. nuxt-og-image APIs 4. nuxt-schema-org APIs 5. nuxt-seo-utils APIs 6. nuxt-site-config APIs
---
nuxt-robots APIs
useRobotsRule(rule: string)
Set robots meta tag for the current page.
Parameters:
rule(string): Robots directives (e.g., "noindex, nofollow")
Example:
<script setup>
useRobotsRule('noindex, nofollow')
</script>---
useBotDetection()
Client-side bot detection.
Returns:
isBot(boolean): Whether current request is from a botname(string): Name of detected bot
Example:
<script setup>
const { isBot, name } = useBotDetection()
if (isBot) {
console.log(`Bot detected: ${name}`)
}
</script>---
getBotDetection(event)
Server-side bot detection.
Parameters:
event(H3Event): HTTP event object
Returns:
isBot(boolean): Whether request is from a botname(string): Bot name
Example:
// server/api/example.ts
export default defineEventHandler((event) => {
const { isBot, name } = getBotDetection(event)
if (isBot) {
// Serve optimized content for bots
}
})---
getPathRobotConfig(path: string)
Get robots configuration for a specific path.
Parameters:
path(string): URL path
Returns: Robots configuration object
Example:
const config = getPathRobotConfig('/admin')---
getSiteRobotConfig()
Get site-wide robots configuration.
Returns: Site robots configuration
Example:
const siteConfig = getSiteRobotConfig()---
nuxt-sitemap APIs
defineSitemapEventHandler(handler)
Define a dynamic sitemap source.
Parameters:
handler(Function): Async function returning sitemap entries
Returns: Event handler
Example:
// server/api/__sitemap__/products.ts
export default defineSitemapEventHandler(async () => {
const products = await fetchProducts()
return products.map(product => ({
loc: `/products/${product.slug}`,
lastmod: product.updatedAt,
changefreq: 'weekly',
priority: 0.8,
images: product.images.map(img => ({
loc: img.url,
caption: img.alt
}))
}))
})Sitemap Entry Format:
interface SitemapEntry {
loc: string // URL path (required)
lastmod?: string | Date // Last modified date
changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never'
priority?: number // 0.0 to 1.0
images?: Array<{
loc: string // Image URL
caption?: string
title?: string
geoLocation?: string
license?: string
}>
videos?: Array<{
title: string
description?: string
thumbnailLoc: string
contentLoc?: string
duration?: number
publicationDate?: string
}>
news?: {
publication: {
name: string
language: string
}
publicationDate: string
title: string
}
}---
nuxt-og-image APIs
defineOgImage(options)
Define an OG image with simple options.
Parameters:
interface OgImageOptions {
title?: string
description?: string
theme?: string
image?: string
price?: string
renderer?: 'satori' | 'chromium'
format?: 'png' | 'jpeg'
quality?: number // 0-100 for JPEG
// ... other custom options
}Example:
<script setup>
defineOgImage({
title: 'My Page Title',
description: 'Page description',
theme: '#00DC82',
format: 'png'
})
</script>---
defineOgImageComponent(component, props)
Use a Vue component as OG image template.
Parameters:
component(string): Component name (e.g., "OgImage")props(object): Props to pass to component
Example:
<script setup>
defineOgImageComponent('BlogPost', {
title: 'Blog Post Title',
author: 'Author Name',
date: '2025-01-10',
image: 'https://example.com/cover.jpg'
})
</script>---
defineOgImageScreenshot(options)
Capture a screenshot of page element as OG image.
Parameters:
interface OgImageScreenshotOptions {
selector: string // CSS selector
width?: number
height?: number
delay?: number
}Example:
<script setup>
defineOgImageScreenshot({
selector: '#og-preview',
width: 1200,
height: 630
})
</script>---
nuxt-schema-org APIs
useSchemaOrg(nodes)
Add Schema.org structured data to the current page.
Parameters:
nodes(Array): Schema.org JSON-LD objects
Returns: void
Example - Article:
<script setup>
useSchemaOrg([
{
'@type': 'Article',
headline: 'Article Title',
author: {
'@type': 'Person',
name: 'Author Name'
},
datePublished: '2025-01-10',
image: 'https://example.com/image.jpg'
}
])
</script>Example - Product:
<script setup>
useSchemaOrg([
{
'@type': 'Product',
name: 'Product Name',
image: 'https://example.com/product.jpg',
offers: {
'@type': 'Offer',
price: '99.99',
priceCurrency: 'USD',
availability: 'https://schema.org/InStock'
}
}
])
</script>Example - Multiple Schemas:
<script setup>
useSchemaOrg([
{
'@type': 'Organization',
name: 'My Company',
url: 'https://example.com'
},
{
'@type': 'BreadcrumbList',
itemListElement: [
{
'@type': 'ListItem',
position: 1,
name: 'Home',
item: 'https://example.com'
},
{
'@type': 'ListItem',
position: 2,
name: 'Products',
item: 'https://example.com/products'
}
]
}
])
</script>---
nuxt-seo-utils APIs
useBreadcrumbItems()
Generate breadcrumb navigation items from current route.
Returns: Array of breadcrumb items
Example:
<script setup>
const breadcrumbs = useBreadcrumbItems()
// [
// { label: 'Home', to: '/' },
// { label: 'Products', to: '/products' },
// { label: 'Product Name', to: '/products/123' }
// ]
</script>
<template>
<nav aria-label="Breadcrumb">
<ol>
<li v-for="(item, index) in breadcrumbs" :key="index">
<NuxtLink :to="item.to">
{{ item.label }}
</NuxtLink>
</li>
</ol>
</nav>
</template>---
nuxt-site-config APIs
useSiteConfig()
Access site configuration.
Returns: Site configuration object
Example:
<script setup>
const siteConfig = useSiteConfig()
console.log(siteConfig.url) // https://example.com
console.log(siteConfig.name) // My Site
console.log(siteConfig.description) // Site description
console.log(siteConfig.defaultLocale) // en
</script>---
updateSiteConfig(config)
Dynamically update site configuration.
Parameters:
config(object): Partial site configuration
Example:
updateSiteConfig({
name: 'New Site Name',
description: 'New description'
})---
createSitePathResolver()
Create a path resolver with site URL.
Returns: Path resolver function
Example:
const resolvePath = createSitePathResolver()
const fullUrl = resolvePath('/products/123')
// https://example.com/products/123---
useNitroOrigin()
Get the origin URL server-side.
Returns: Origin URL
Example:
// server/api/example.ts
export default defineEventHandler(() => {
const origin = useNitroOrigin()
// https://example.com
})---
Configuration Objects
Site Config
interface SiteConfig {
url: string // Site URL (required)
name: string // Site name
description?: string
defaultLocale?: string // Default language code
identity?: {
type: 'Organization' | 'Person'
name?: string
logo?: string
// ... more identity fields
}
twitter?: string
trailingSlash?: boolean
}---
Robots Config
interface RobotsConfig {
disallow?: string[]
allow?: string[]
groups?: Array<{
userAgent: string[]
allow?: string[]
disallow?: string[]
}>
sitemap?: string | string[]
cleanParam?: string[]
}---
Sitemap Config
interface SitemapConfig {
strictNuxtContentPaths?: boolean
exclude?: string[]
defaults?: {
changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never'
priority?: number
}
sitemaps?: {
[key: string]: {
includeAppSources?: boolean
sources?: string[]
}
}
chunksSize?: number
cacheMaxAgeSeconds?: number
filter?: (entry: SitemapEntry) => boolean
}---
OG Image Config
interface OgImageConfig {
renderer?: 'satori' | 'chromium'
format?: 'png' | 'jpeg'
quality?: number
component?: string
fonts?: Array<string | {
name: string
weight: number
path: string
}>
runtimeCacheStorage?: boolean
}---
Schema.org Config
interface SchemaOrgConfig {
identity?: {
type: 'Organization' | 'Person'
name?: string
url?: string
logo?: string
sameAs?: string[]
// ... more identity fields
}
}---
Link Checker Config
interface LinkCheckerConfig {
enabled?: boolean
showLiveInspections?: boolean
failOnError?: boolean
excludeLinks?: string[]
skipInspections?: Array<'external' | 'mailto' | 'tel'>
}---
Route Rules
Configure SEO per route:
export default defineNuxtConfig({
routeRules: {
'/': {
sitemap: {
changefreq: 'daily',
priority: 1.0
}
},
'/admin/**': {
robots: 'noindex, nofollow',
sitemap: { exclude: true }
}
}
})---
Nuxt SEO Meta (useSeoMeta)
While not part of the 8 modules, useSeoMeta is commonly used:
<script setup>
useSeoMeta({
title: 'Page Title',
description: 'Page description',
ogTitle: 'OG Title',
ogDescription: 'OG Description',
ogImage: 'https://example.com/og-image.png',
ogUrl: 'https://example.com/page',
twitterCard: 'summary_large_image',
twitterSite: '@mysite',
twitterCreator: '@author'
})
</script>---
Last Updated: 2025-11-10 Source: Official Nuxt SEO documentation
Nuxt SEO Best Practices
Complete guide to SEO best practices for Nuxt applications
---
Table of Contents
1. Always Set Site Config 2. Use Environment Variables 3. Configure Robots for Staging 4. Generate Dynamic Sitemaps 5. Optimize OG Images 6. Add Schema.org to All Pages 7. Monitor Link Health 8. Use Breadcrumbs
---
1. Always Set Site Config
Why: Site configuration is the foundation for all SEO modules. Every module relies on site.url, site.name, and other core settings.
site: {
url: process.env.NUXT_PUBLIC_SITE_URL,
name: 'Your Site Name',
description: 'Your site description',
defaultLocale: 'en'
}Critical: Without site.url, sitemaps, OG images, and Schema.org will not work correctly.
---
2. Use Environment Variables
Why: Different environments (development, staging, production) need different configurations. Never hardcode URLs.
# .env
NUXT_PUBLIC_SITE_URL=https://example.com
NUXT_PUBLIC_SITE_NAME=My Site
NUXT_PUBLIC_ENV=productionThen reference in config:
export default defineNuxtConfig({
site: {
url: process.env.NUXT_PUBLIC_SITE_URL,
name: process.env.NUXT_PUBLIC_SITE_NAME
}
})---
3. Configure Robots for Staging
Why: Prevent staging/development sites from appearing in search results. This is a critical mistake that can harm your production SEO.
robots: {
disallow: process.env.NUXT_PUBLIC_ENV === 'staging' ? ['/'] : []
}Production: Allows crawling Staging: Blocks all crawling Development: Can block all (optional)
---
4. Generate Dynamic Sitemaps
Why: Static sitemaps become outdated. Dynamic sitemaps automatically include new content from your CMS/database.
// server/api/__sitemap__/posts.ts
export default defineSitemapEventHandler(async () => {
const posts = await fetchAllPosts()
return posts.map(post => ({
loc: `/blog/${post.slug}`,
lastmod: post.updatedAt
}))
})Configure in nuxt.config.ts:
sitemap: {
sitemaps: {
posts: {
sources: ['/api/__sitemap__/posts']
}
}
}---
5. Optimize OG Images
Why: Large OG images slow down social sharing. Choose the right renderer and format for your needs.
ogImage: {
renderer: 'satori', // Faster, no runtime
format: 'png',
fonts: ['Inter:400', 'Inter:700']
}Satori (recommended):
- Fast, zero runtime overhead
- Good for text-heavy designs
- Limited CSS support
Chromium:
- Full CSS support
- Slower, requires runtime
- Use for complex layouts
---
6. Add Schema.org to All Pages
Why: Structured data improves search appearance with rich snippets, knowledge panels, and enhanced results.
Every page should have appropriate structured data:
| Page Type | Schema Type |
|---|---|
| Home | Organization/Person |
| Blog Posts | BlogPosting/Article |
| Products | Product |
| About | Organization/Person |
| Contact | ContactPage |
| FAQ | FAQPage |
| Events | Event |
Example for blog posts:
<script setup>
useSchemaOrg([
{
'@type': 'BlogPosting',
headline: 'How to Build Amazing Apps',
author: {
'@type': 'Person',
name: 'Jane Doe'
},
datePublished: '2025-01-10',
dateModified: '2025-01-11'
}
])
</script>---
7. Monitor Link Health
Why: Broken links harm SEO and user experience. Link checker catches issues during development and build.
linkChecker: {
enabled: true,
showLiveInspections: true
}Development: Shows warnings in console Build: Can fail build on errors (optional) Production: Should be disabled for performance
---
8. Use Breadcrumbs
Why: Breadcrumbs improve navigation and provide structured data for search engines.
<script setup>
const breadcrumbs = useBreadcrumbItems()
useSchemaOrg([
{
'@type': 'BreadcrumbList',
itemListElement: breadcrumbs.map((item, index) => ({
'@type': 'ListItem',
position: index + 1,
name: item.label,
item: item.to
}))
}
])
</script>
<template>
<nav aria-label="Breadcrumb">
<ol>
<li v-for="(item, index) in breadcrumbs" :key="index">
<NuxtLink :to="item.to">
{{ item.label }}
</NuxtLink>
</li>
</ol>
</nav>
</template>Benefits:
- Better user navigation
- Rich snippets in search results
- Improved crawling efficiency
---
Additional Best Practices
Multi-Language SEO
For i18n sites:
site: {
defaultLocale: 'en',
locales: ['en', 'es', 'fr']
}Performance
- Use
satorirenderer for OG images (faster) - Enable sitemap caching for large sites
- Disable link checker in production
Testing Checklist
Before deploying:
- [ ]
site.urlis set to production URL - [ ] Staging robots.txt blocks crawling
- [ ] Sitemap accessible at
/sitemap.xml - [ ] OG images render correctly (test with OpenGraph.xyz)
- [ ] Schema.org validates (use Google Rich Results Test)
- [ ] No broken links in build output
- [ ] Breadcrumbs work on all pages
- [ ] Meta tags correct on all pages
---
Last Updated: 2025-11-27
Nuxt SEO - Common Patterns
Real-world usage patterns and examples for Nuxt SEO modules.
---
Table of Contents
1. Blog Site Pattern 2. E-commerce Pattern 3. Multi-Language Pattern 4. Corporate Website Pattern 5. Environment-Based Configuration 6. Custom OG Image Component Pattern 7. Breadcrumbs with Schema.org Pattern 8. FAQ Page Pattern
---
Blog Site Pattern
Complete Setup
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/seo'],
site: {
url: process.env.NUXT_PUBLIC_SITE_URL,
name: 'My Blog', // Required in v5
description: 'Thoughts and tutorials',
defaultLocale: 'en'
},
robots: {
disallow: ['/admin', '/drafts'],
sitemap: `${process.env.NUXT_PUBLIC_SITE_URL}/sitemap.xml`
},
sitemap: {
strictNuxtContentPaths: true,
sitemaps: {
pages: {
includeAppSources: true
},
posts: {
sources: ['/api/__sitemap__/posts']
}
},
defaults: {
changefreq: 'weekly',
priority: 0.7
}
},
ogImage: {
renderer: 'satori',
fonts: ['Inter:400', 'Inter:700']
},
schemaOrg: {
identity: {
type: 'Person',
name: 'Your Name',
url: process.env.NUXT_PUBLIC_SITE_URL
}
}
})Blog Post Page
<!-- pages/blog/[slug].vue -->
<script setup>
const route = useRoute()
const { data: post } = await useFetch(`/api/posts/${route.params.slug}`)
// Meta tags
useSeoMeta({
title: post.value.title,
description: post.value.excerpt,
ogTitle: post.value.title,
ogDescription: post.value.excerpt,
ogImage: post.value.coverImage,
ogType: 'article',
twitterCard: 'summary_large_image'
})
// OG Image
defineOgImageComponent('BlogPost', {
title: post.value.title,
author: post.value.author.name,
date: post.value.publishedAt,
coverImage: post.value.coverImage
})
// Schema.org
useSchemaOrg([
{
'@type': 'BlogPosting',
headline: post.value.title,
image: post.value.coverImage,
datePublished: post.value.publishedAt,
dateModified: post.value.updatedAt,
author: {
'@type': 'Person',
name: post.value.author.name,
url: post.value.author.url
},
publisher: {
'@type': 'Person',
name: 'Your Name'
},
description: post.value.excerpt,
articleBody: post.value.content
}
])
</script>
<template>
<article>
<h1>{{ post.title }}</h1>
<div v-html="post.content" />
</article>
</template>Dynamic Blog Sitemap
// server/api/__sitemap__/posts.ts
export default defineSitemapEventHandler(async () => {
const posts = await $fetch('/api/posts')
return posts.map(post => ({
loc: `/blog/${post.slug}`,
lastmod: post.updatedAt,
changefreq: 'monthly',
priority: 0.8
}))
})---
E-commerce Pattern
Complete Setup
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/seo'],
site: {
url: process.env.NUXT_PUBLIC_SITE_URL,
name: 'My Store',
description: 'Quality products'
},
robots: {
disallow: ['/admin', '/checkout', '/account']
},
sitemap: {
sitemaps: {
pages: {
includeAppSources: true,
exclude: ['/checkout/**', '/account/**']
},
products: {
sources: ['/api/__sitemap__/products'],
defaults: {
changefreq: 'daily',
priority: 0.9
}
},
categories: {
sources: ['/api/__sitemap__/categories'],
defaults: {
changefreq: 'weekly',
priority: 0.7
}
}
},
chunksSize: 1000
},
schemaOrg: {
identity: {
type: 'Organization',
name: 'My Store',
url: process.env.NUXT_PUBLIC_SITE_URL,
logo: `${process.env.NUXT_PUBLIC_SITE_URL}/logo.png`
}
}
})Product Page
<!-- pages/products/[id].vue -->
<script setup>
const route = useRoute()
const product = await $fetch(`/api/products/${route.params.id}`)
// Meta tags
useSeoMeta({
title: `${product.name} - Buy Now`,
description: product.description,
ogImage: product.mainImage,
ogType: 'product'
})
// OG Image
defineOgImage({
title: product.name,
description: product.tagline,
image: product.mainImage,
price: `$${product.price}`
})
// Schema.org Product
useSchemaOrg([
{
'@type': 'Product',
name: product.name,
image: product.images,
description: product.description,
sku: product.sku,
brand: {
'@type': 'Brand',
name: product.brand
},
offers: {
'@type': 'Offer',
url: `${useSiteConfig().url}/products/${product.id}`,
priceCurrency: 'USD',
price: product.price,
availability: product.inStock
? 'https://schema.org/InStock'
: 'https://schema.org/OutOfStock',
seller: {
'@type': 'Organization',
name: 'My Store'
}
},
aggregateRating: product.rating && {
'@type': 'AggregateRating',
ratingValue: product.rating.average,
reviewCount: product.rating.count,
bestRating: 5,
worstRating: 1
}
},
{
'@type': 'BreadcrumbList',
itemListElement: [
{
'@type': 'ListItem',
position: 1,
name: 'Home',
item: useSiteConfig().url
},
{
'@type': 'ListItem',
position: 2,
name: product.category.name,
item: `${useSiteConfig().url}/categories/${product.category.slug}`
},
{
'@type': 'ListItem',
position: 3,
name: product.name,
item: `${useSiteConfig().url}/products/${product.id}`
}
]
}
])
</script>
<template>
<div>
<h1>{{ product.name }}</h1>
<p>{{ product.description }}</p>
<div>Price: ${{ product.price }}</div>
</div>
</template>Products Sitemap
// server/api/__sitemap__/products.ts
export default defineSitemapEventHandler(async () => {
const products = await $fetch('/api/products')
return products.map(product => ({
loc: `/products/${product.id}`,
lastmod: product.updatedAt,
changefreq: 'daily',
priority: 0.9,
images: product.images.map(img => ({
loc: img.url,
caption: img.alt,
title: product.name
}))
}))
})---
Multi-Language Pattern
Setup
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/seo', '@nuxtjs/i18n'],
i18n: {
locales: [
{ code: 'en', name: 'English', file: 'en.json' },
{ code: 'fr', name: 'Français', file: 'fr.json' },
{ code: 'es', name: 'Español', file: 'es.json' }
],
defaultLocale: 'en',
strategy: 'prefix'
},
site: {
url: process.env.NUXT_PUBLIC_SITE_URL,
name: 'My Site',
defaultLocale: 'en'
},
ogImage: {
fonts: [
'Inter:400',
'Inter:700',
'Noto Sans SC:400', // Chinese
'Noto Sans JP:400', // Japanese
'Noto Sans KR:400' // Korean
]
}
})Localized Page
<script setup>
const { locale, t } = useI18n()
const localePath = useLocalePath()
useSeoMeta({
title: t('home.title'),
description: t('home.description'),
ogLocale: locale.value
})
useSchemaOrg([
{
'@type': 'WebPage',
name: t('home.title'),
description: t('home.description'),
inLanguage: locale.value
}
])
</script>---
Corporate Website Pattern
// nuxt.config.ts
export default defineNuxtConfig({
modules: [
'nuxt-site-config',
'@nuxtjs/robots',
'@nuxtjs/sitemap',
'nuxt-og-image',
'nuxt-schema-org'
],
site: {
url: process.env.NUXT_PUBLIC_SITE_URL,
name: 'My Company',
description: 'Leading provider of...'
},
robots: {
disallow: ['/admin', '/internal']
},
sitemap: {
defaults: {
changefreq: 'monthly',
priority: 0.7
}
},
schemaOrg: {
identity: {
type: 'Organization',
name: 'My Company',
url: process.env.NUXT_PUBLIC_SITE_URL,
logo: `${process.env.NUXT_PUBLIC_SITE_URL}/logo.png`,
sameAs: [
'https://twitter.com/mycompany',
'https://facebook.com/mycompany',
'https://linkedin.com/company/mycompany'
],
contactPoint: [
{
'@type': 'ContactPoint',
telephone: '+1-555-123-4567',
contactType: 'customer service',
email: 'support@example.com',
availableLanguage: ['en', 'es']
}
]
}
},
routeRules: {
'/': { sitemap: { changefreq: 'weekly', priority: 1.0 } },
'/about': { sitemap: { changefreq: 'monthly', priority: 0.8 } },
'/contact': { sitemap: { changefreq: 'monthly', priority: 0.8 } }
}
})---
Environment-Based Configuration
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/seo'],
site: {
url: process.env.NUXT_PUBLIC_SITE_URL || 'http://localhost:3000'
},
robots: {
// Block all in development/staging
disallow: ['development', 'staging'].includes(process.env.NUXT_PUBLIC_ENV || '')
? ['/']
: ['/admin'],
// Only include sitemap in production
sitemap: process.env.NUXT_PUBLIC_ENV === 'production'
? `${process.env.NUXT_PUBLIC_SITE_URL}/sitemap.xml`
: undefined
},
sitemap: {
// Disable in non-production
enabled: process.env.NUXT_PUBLIC_ENV === 'production'
},
linkChecker: {
// Enable only in development
enabled: process.env.NODE_ENV === 'development',
showLiveInspections: true,
failOnError: false
}
})---
Custom OG Image Component Pattern
Component
<!-- components/OgImage.vue -->
<template>
<div class="w-[1200px] h-[630px] flex flex-col justify-between p-16 bg-gradient-to-br from-blue-600 to-purple-700">
<!-- Header -->
<div class="flex items-center justify-between">
<img v-if="logo" :src="logo" class="h-20" />
<div class="text-2xl text-white/80">{{ siteName }}</div>
</div>
<!-- Content -->
<div class="flex-1 flex items-center">
<div>
<h1 class="text-7xl font-bold text-white mb-6 leading-tight">
{{ title }}
</h1>
<p v-if="description" class="text-3xl text-white/90">
{{ description }}
</p>
</div>
</div>
<!-- Footer -->
<div class="flex items-center justify-between text-white/70 text-xl">
<div v-if="author">
By {{ author }}
</div>
<div v-if="date">
{{ formatDate(date) }}
</div>
</div>
</div>
</template>
<script setup>
defineProps({
title: String,
description: String,
siteName: String,
logo: String,
author: String,
date: String
})
const formatDate = (date: string) => {
return new Date(date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
})
}
</script>Usage
<script setup>
defineOgImageComponent('OgImage', {
title: 'My Page Title',
description: 'Page description',
siteName: 'My Site',
logo: 'https://example.com/logo.png',
author: 'John Doe',
date: '2025-01-10'
})
</script>---
Breadcrumbs with Schema.org Pattern
<script setup>
const breadcrumbs = useBreadcrumbItems()
// Add breadcrumb schema
useSchemaOrg([
{
'@type': 'BreadcrumbList',
itemListElement: breadcrumbs.map((item, index) => ({
'@type': 'ListItem',
position: index + 1,
name: item.label,
item: `${useSiteConfig().url}${item.to}`
}))
}
])
</script>
<template>
<nav aria-label="Breadcrumb" class="mb-4">
<ol class="flex items-center space-x-2 text-sm text-gray-600">
<li v-for="(item, index) in breadcrumbs" :key="index" class="flex items-center">
<NuxtLink :to="item.to" class="hover:text-gray-900">
{{ item.label }}
</NuxtLink>
<span v-if="index < breadcrumbs.length - 1" class="mx-2">/</span>
</li>
</ol>
</nav>
</template>---
FAQ Page Pattern
<script setup>
const faqs = [
{
question: 'What is Nuxt?',
answer: 'Nuxt is a Vue.js framework for building web applications.'
},
{
question: 'How do I install Nuxt?',
answer: 'Run: npx nuxi@latest init my-app'
}
]
// FAQ Schema
useSchemaOrg([
{
'@type': 'FAQPage',
mainEntity: faqs.map(faq => ({
'@type': 'Question',
name: faq.question,
acceptedAnswer: {
'@type': 'Answer',
text: faq.answer
}
}))
}
])
</script>
<template>
<div>
<h1>Frequently Asked Questions</h1>
<div v-for="(faq, index) in faqs" :key="index">
<h2>{{ faq.question }}</h2>
<p>{{ faq.answer }}</p>
</div>
</div>
</template>---
Last Updated: 2025-11-10 Usage: Copy and adapt these patterns for your specific needs
Nuxt SEO - Installation Guide
Step-by-step installation patterns for all package managers.
---
Table of Contents
1. Quick Install (Recommended) 2. Individual Modules 3. Scenario-Based Installation 4. Multi-Language Installation 5. Environment-Based Installation 6. Manual Package Installation 7. Verification Steps 8. Troubleshooting 9. Migration from Individual Modules
---
Quick Install (Recommended)
Complete Bundle
Install all 8 modules at once:
# Recommended (v5)
npx nuxt module add seo
# Legacy command (still works)
npx nuxi module add @nuxtjs/seoAdd to nuxt.config.ts:
export default defineNuxtConfig({
modules: ['@nuxtjs/seo']
})---
Individual Modules
Install only what you need:
Install Each Module
npx nuxt module add @nuxtjs/robots
npx nuxt module add @nuxtjs/sitemap
npx nuxt module add nuxt-og-image
npx nuxt module add nuxt-schema-org
npx nuxt module add nuxt-link-checker
npx nuxt module add nuxt-seo-utils
npx nuxt module add nuxt-site-configConfigure in nuxt.config.ts
export default defineNuxtConfig({
modules: [
'nuxt-site-config', // Base config (install first)
'@nuxtjs/robots',
'@nuxtjs/sitemap',
'nuxt-og-image',
'nuxt-schema-org',
'nuxt-link-checker',
'nuxt-seo-utils'
]
})---
Scenario-Based Installation
Blog Site
npx nuxt module add @nuxtjs/seo
# Or individually
npx nuxt module add @nuxtjs/robots
npx nuxt module add @nuxtjs/sitemap
npx nuxt module add nuxt-og-image
npx nuxt module add nuxt-schema-org
npx nuxt module add nuxt-site-configexport default defineNuxtConfig({
modules: ['@nuxtjs/seo'],
site: {
url: process.env.NUXT_PUBLIC_SITE_URL,
name: 'My Blog', // Required in v5 - no longer auto-inferred
},
robots: {
disallow: ['/admin', '/drafts']
},
sitemap: {
sources: ['/api/__sitemap__/posts']
},
ogImage: {
fonts: ['Inter:400', 'Inter:700']
},
schemaOrg: {
identity: {
type: 'Person',
name: 'Your Name'
}
}
})E-commerce Site
npx nuxt module add @nuxtjs/seoexport default defineNuxtConfig({
modules: ['@nuxtjs/seo'],
site: {
url: process.env.NUXT_PUBLIC_SITE_URL,
name: 'My Store'
},
sitemap: {
sitemaps: {
pages: { includeAppSources: true },
products: { sources: ['/api/__sitemap__/products'] },
categories: { sources: ['/api/__sitemap__/categories'] }
}
},
schemaOrg: {
identity: {
type: 'Organization',
name: 'My Company'
}
}
})Corporate Site
npx nuxt module add @nuxtjs/robots
npx nuxt module add @nuxtjs/sitemap
npx nuxt module add nuxt-og-image
npx nuxt module add nuxt-schema-org
npx nuxt module add nuxt-site-configexport default defineNuxtConfig({
modules: [
'nuxt-site-config',
'@nuxtjs/robots',
'@nuxtjs/sitemap',
'nuxt-og-image',
'nuxt-schema-org'
],
site: {
url: process.env.NUXT_PUBLIC_SITE_URL,
name: 'My Company'
},
schemaOrg: {
identity: {
type: 'Organization',
name: 'My Company',
logo: `${process.env.NUXT_PUBLIC_SITE_URL}/logo.png`,
sameAs: [
'https://twitter.com/mycompany',
'https://linkedin.com/company/mycompany'
]
}
}
})---
Multi-Language Installation
npx nuxt module add @nuxtjs/seo
npx nuxt module add @nuxtjs/i18nexport default defineNuxtConfig({
modules: ['@nuxtjs/seo', '@nuxtjs/i18n'],
i18n: {
locales: [
{ code: 'en', name: 'English' },
{ code: 'fr', name: 'Français' },
{ code: 'es', name: 'Español' }
],
defaultLocale: 'en'
},
site: {
url: process.env.NUXT_PUBLIC_SITE_URL,
defaultLocale: 'en'
}
})---
Environment-Based Installation
Development
export default defineNuxtConfig({
modules: ['@nuxtjs/seo'],
robots: {
// Block all in development
disallow: process.env.NODE_ENV === 'development' ? ['/'] : []
},
linkChecker: {
// Enable in development only
enabled: process.env.NODE_ENV === 'development',
showLiveInspections: true
}
})Staging
export default defineNuxtConfig({
modules: ['@nuxtjs/seo'],
robots: {
// Block staging from search engines
disallow: process.env.NUXT_PUBLIC_ENV === 'staging' ? ['/'] : []
},
sitemap: {
// Don't index staging in sitemap
enabled: process.env.NUXT_PUBLIC_ENV !== 'staging'
}
})Production
export default defineNuxtConfig({
modules: ['@nuxtjs/seo'],
site: {
url: process.env.NUXT_PUBLIC_SITE_URL // Production URL
},
robots: {
disallow: ['/admin'] // Only block admin
},
linkChecker: {
enabled: false // Disable in production
}
})---
Manual Package Installation
If nuxi module add doesn't work:
# Bun
bun add @nuxtjs/seo
# npm
npm install @nuxtjs/seo
# pnpm
pnpm add @nuxtjs/seoThen add to nuxt.config.ts:
export default defineNuxtConfig({
modules: ['@nuxtjs/seo']
})---
Verification Steps
After installation:
1. Restart dev server:
npm run dev2. Verify robots.txt:
- Visit:
http://localhost:3000/robots.txt - Should see generated robots.txt
3. Verify sitemap:
- Visit:
http://localhost:3000/sitemap.xml - Should see XML sitemap
4. Check module loading:
# Should list all SEO modules
npx nuxt info5. Test OG image:
- Add
defineOgImage()to a page - Visit:
http://localhost:3000/__og-image__/og.png
---
Troubleshooting
Module not found
# Clear cache and reinstall
rm -rf .nuxt node_modules/.cache
npm installrobots.txt not generating
1. Check site.url is set in config 2. Verify @nuxtjs/robots is in modules array 3. Clear .nuxt cache
Sitemap not generating
1. Check site.url is set 2. Verify @nuxtjs/sitemap is installed 3. Restart dev server
Build errors
1. Update to latest versions:
npm update @nuxtjs/seo2. Clear cache:
rm -rf .nuxt node_modules/.cache3. Reinstall:
rm -rf node_modules
npm install---
Migration from Individual Modules
If you have individual modules installed:
Before
export default defineNuxtConfig({
modules: [
'@nuxtjs/robots',
'@nuxtjs/sitemap',
'nuxt-og-image',
'nuxt-schema-org'
]
})After (Recommended)
export default defineNuxtConfig({
modules: ['@nuxtjs/seo']
})Remove individual packages:
# Bun
bun remove @nuxtjs/robots @nuxtjs/sitemap nuxt-og-image nuxt-schema-org
# npm
npm uninstall @nuxtjs/robots @nuxtjs/sitemap nuxt-og-image nuxt-schema-orgInstall bundle:
npx nuxt module add @nuxtjs/seo---
Last Updated: 2026-03-30 Package Manager: npm (primary) Nuxt Version: >= 3.0.0 or Nuxt 4.x v5 Install: npx nuxt module add seo
Nuxt SEO Module Details
Complete documentation for all 8 Nuxt SEO modules
---
Table of Contents
1. Module 1: @nuxtjs/seo (Primary SEO Module) 2. Module 2: nuxt-robots (Robots.txt & Bot Detection) 3. Module 3: nuxt-sitemap (XML Sitemap Generation) 4. Module 4: nuxt-og-image (Open Graph Image Generation) 5. Module 5: nuxt-schema-org (Schema.org Structured Data) 6. Module 6: nuxt-link-checker (Link Validation) 7. Module 7: nuxt-seo-utils (SEO Utilities) 8. Module 8: nuxt-site-config (Site Configuration)
---
Module 1: @nuxtjs/seo (Primary SEO Module)
Version: v5.1.0 | Downloads: 4.2M/month | Stars: 3,300
Purpose
Primary SEO module that provides foundational features and installs all 8 modules as a bundle when used.
Configuration
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/seo'],
site: {
url: 'https://example.com',
name: 'My Awesome Site',
description: 'Welcome to my awesome site!',
defaultLocale: 'en'
}
})Key Features
- Installs all 8 SEO modules with sensible defaults
- Provides unified configuration through
siteobject - Integrates with Nuxt Content for automatic SEO
- Handles meta tags, titles, and descriptions automatically
---
Module 2: nuxt-robots (Robots.txt & Bot Detection)
Version: v6.0.6 | Downloads: 8.7M
Purpose
Manages robots crawling your site with minimal configuration and best practice defaults. Controls which pages search engines can crawl and provides bot detection capabilities.
Basic Configuration
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/robots'],
robots: {
// Site-wide rules
disallow: ['/admin', '/private'],
// User-agent specific rules
groups: [
{
userAgent: ['Googlebot'],
allow: ['/'],
disallow: ['/admin']
},
{
userAgent: ['Bingbot'],
allow: ['/']
}
],
// Sitemap reference
sitemap: 'https://example.com/sitemap.xml',
// Clean params for Yandex
cleanParam: ['utm_source', 'utm_medium', 'utm_campaign']
}
})Page-Level Control
<script setup>
// Block indexing on this page
defineRouteRules({
robots: 'noindex, nofollow'
})
// Or use composable
useRobotsRule('noindex, nofollow')
</script>Bot Detection
Server-side:
// server/api/example.ts
export default defineEventHandler((event) => {
const botDetection = getBotDetection(event)
if (botDetection.isBot) {
console.log('Bot detected:', botDetection.name)
// Serve optimized content for bots
}
})Client-side:
<script setup>
const { isBot, name } = useBotDetection()
if (isBot) {
console.log('Bot detected:', name)
}
</script>APIs
- `useRobotsRule(rule: string)`: Set robots meta tag for current page
- `useBotDetection()`: Client-side bot detection
- `getBotDetection(event)`: Server-side bot detection
- `getPathRobotConfig(path: string)`: Get robots config for specific path
- `getSiteRobotConfig()`: Get site-wide robots config
Common Patterns
Development Mode - Block All:
robots: {
disallow: process.env.NODE_ENV === 'development' ? ['/'] : []
}Staging Environment - Block Completely:
robots: {
disallow: process.env.NUXT_PUBLIC_ENV === 'staging' ? ['/'] : []
}---
Module 3: nuxt-sitemap (XML Sitemap Generation)
Version: v8.0.11 | Downloads: 10M
Purpose
Powerfully flexible XML sitemaps that integrate seamlessly with your Nuxt app. Generates sitemaps automatically from routes with support for dynamic URLs, multiple sitemaps, media, and advanced optimization.
Basic Configuration
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/sitemap'],
site: {
url: 'https://example.com'
},
sitemap: {
// Automatically includes all routes
strictNuxtContentPaths: true,
// Exclude specific URLs
exclude: [
'/admin/**',
'/private/**'
],
// Default values for all URLs
defaults: {
changefreq: 'daily',
priority: 0.7
}
}
})Multiple Sitemaps
sitemap: {
sitemaps: {
pages: {
includeAppSources: true
},
products: {
sources: [
'/api/__sitemap__/products'
]
},
blog: {
sources: [
'/api/__sitemap__/blog'
]
}
}
}Dynamic URLs from API
Create /server/api/__sitemap__/products.ts:
export default defineSitemapEventHandler(async () => {
const products = await fetchProducts()
return products.map(product => ({
loc: `/products/${product.slug}`,
lastmod: product.updatedAt,
changefreq: 'weekly',
priority: 0.8,
// Add images
images: product.images.map(img => ({
loc: img.url,
caption: img.alt
}))
}))
})Sitemap Index (for large sites)
sitemap: {
sitemaps: true,
chunksSize: 1000, // Split into chunks of 1000 URLs
}Generates:
/sitemap_index.xml
/sitemap-0.xml
/sitemap-1.xml
/sitemap-2.xmlRoute Rules
export default defineNuxtConfig({
routeRules: {
'/': { sitemap: { changefreq: 'daily', priority: 1.0 } },
'/about': { sitemap: { changefreq: 'monthly', priority: 0.8 } },
'/admin/**': { sitemap: { exclude: true } }
}
})Media Support
Images:
{
loc: '/products/awesome-product',
images: [
{
loc: 'https://example.com/images/product.jpg',
caption: 'Product image',
title: 'Awesome Product',
geoLocation: 'New York, USA',
license: 'https://example.com/license'
}
]
}Videos:
{
loc: '/videos/tutorial',
videos: [
{
title: 'How to Use Our Product',
description: 'A comprehensive tutorial',
thumbnailLoc: 'https://example.com/thumb.jpg',
contentLoc: 'https://example.com/video.mp4',
duration: 600
}
]
}Google Search Console Submission
After deploying:
1. Go to Google Search Console 2. Select your property 3. Navigate to Sitemaps in left menu 4. Enter sitemap URL: https://example.com/sitemap.xml 5. Click Submit
---
Module 4: nuxt-og-image (Open Graph Image Generation)
Version: v6.3.1 | Downloads: 3.7M
Purpose
Generate Open Graph images dynamically using Vue templates. Creates beautiful social sharing previews for Twitter, Facebook, LinkedIn with zero runtime overhead.
Basic Usage
<script setup>
defineOgImage({
title: 'Welcome to My Site',
description: 'Building amazing web experiences',
theme: '#00DC82'
})
</script>
<template>
<div>
<h1>Welcome</h1>
</div>
</template>Generates: https://example.com/__og-image__/og.png
Configuration
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-og-image'],
ogImage: {
// Rendering engine: 'satori' (default) or 'chromium'
renderer: 'satori',
// Image format: 'png' or 'jpeg'
format: 'png',
// Quality for JPEG (0-100)
quality: 90,
// Default component
component: 'OgImage',
// Custom fonts
fonts: [
'Inter:400',
'Inter:700'
]
}
})Vue Component Templates
Create /components/OgImage.vue:
<template>
<div class="w-full h-full flex flex-col justify-between p-12 bg-gradient-to-br from-blue-500 to-purple-600">
<div>
<h1 class="text-6xl font-bold text-white mb-4">
{{ title }}
</h1>
<p class="text-2xl text-white/90">
{{ description }}
</p>
</div>
<div class="flex items-center">
<img v-if="logo" :src="logo" class="w-16 h-16 mr-4" />
<div class="text-xl text-white">
{{ siteName }}
</div>
</div>
</div>
</template>
<script setup>
defineProps({
title: String,
description: String,
siteName: String,
logo: String
})
</script>Use in page:
<script setup>
defineOgImageComponent('OgImage', {
title: 'Custom OG Image',
description: 'Built with Vue templates',
siteName: 'My Awesome Site',
logo: 'https://example.com/logo.png'
})
</script>Screenshot Mode
<script setup>
defineOgImageScreenshot({
selector: '#og-preview', // CSS selector to capture
width: 1200,
height: 630
})
</script>
<template>
<div>
<div id="og-preview" class="hidden">
<!-- Content to capture -->
<h1>Page Title</h1>
</div>
<div>
<!-- Actual page content -->
</div>
</div>
</template>Custom Fonts
// nuxt.config.ts
ogImage: {
fonts: [
// Google Fonts
'Inter:400',
'Inter:700',
'Roboto:400',
// Local fonts
{
name: 'MyFont',
weight: 400,
path: '/fonts/myfont.ttf'
}
]
}APIs
- `defineOgImage(options)`: Define OG image with options
- `defineOgImageComponent(component, props)`: Use Vue component
- `defineOgImageScreenshot(options)`: Capture screenshot
---
Module 5: nuxt-schema-org (Schema.org Structured Data)
Version: v6.0.4 | Downloads: 3.9M
Purpose
Build Schema.org graphs for enhanced search results with rich snippets, knowledge panels, and better SEO.
Basic Configuration
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-schema-org'],
site: {
url: 'https://example.com',
name: 'My Awesome Site'
},
schemaOrg: {
identity: {
type: 'Organization',
name: 'My Company',
url: 'https://example.com',
logo: 'https://example.com/logo.png'
}
}
})Usage in Pages
<script setup>
useSchemaOrg([
{
'@type': 'Article',
headline: 'How to Build Amazing Web Apps',
author: {
'@type': 'Person',
name: 'Jane Doe'
},
datePublished: '2025-01-10',
dateModified: '2025-01-11',
image: 'https://example.com/article-image.jpg',
description: 'Learn how to build amazing web applications.'
}
])
</script>Common Schema Types
Organization:
useSchemaOrg([
{
'@type': 'Organization',
name: 'My Company',
url: 'https://example.com',
logo: 'https://example.com/logo.png',
sameAs: [
'https://twitter.com/mycompany',
'https://facebook.com/mycompany'
],
contactPoint: {
'@type': 'ContactPoint',
telephone: '+1-555-123-4567',
contactType: 'customer service'
}
}
])Product:
useSchemaOrg([
{
'@type': 'Product',
name: 'Amazing Product',
image: 'https://example.com/product.jpg',
description: 'The best product ever made',
brand: {
'@type': 'Brand',
name: 'My Brand'
},
offers: {
'@type': 'Offer',
url: 'https://example.com/products/amazing-product',
priceCurrency: 'USD',
price: '99.99',
availability: 'https://schema.org/InStock'
},
aggregateRating: {
'@type': 'AggregateRating',
ratingValue: '4.8',
reviewCount: '127'
}
}
])FAQ:
useSchemaOrg([
{
'@type': 'FAQPage',
mainEntity: [
{
'@type': 'Question',
name: 'What is Nuxt?',
acceptedAnswer: {
'@type': 'Answer',
text: 'Nuxt is a Vue.js framework for building web applications.'
}
}
]
}
])APIs
- `useSchemaOrg(nodes)`: Add Schema.org structured data to page
---
Module 6: nuxt-link-checker (Link Validation)
Version: v5.0.6 | Downloads: 2.8M
Purpose
Find and fix links that may negatively affect SEO. Detects broken links, redirects, and issues during development and build.
Configuration
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-link-checker'],
linkChecker: {
enabled: true,
showLiveInspections: true,
failOnError: false,
excludeLinks: [
'https://example.com/temp/*'
],
skipInspections: ['external']
}
})Features
- Detects 404 (broken) links
- Identifies redirect chains
- Finds malformed URLs
- Checks internal and external links
- Validates anchor links (#hash)
- DevTools integration
- Build-time scanning
---
Module 7: nuxt-seo-utils (SEO Utilities)
Version: v8.1.4 | Downloads: 2.2M
Purpose
SEO utilities for discoverability and shareability including canonical URLs, breadcrumbs, app icons, and Open Graph automation.
Breadcrumb Utilities
<script setup>
const breadcrumbs = useBreadcrumbItems()
</script>
<template>
<nav aria-label="Breadcrumb">
<ol>
<li v-for="(item, index) in breadcrumbs" :key="index">
<NuxtLink :to="item.to">
{{ item.label }}
</NuxtLink>
</li>
</ol>
</nav>
</template>APIs
- `useBreadcrumbItems()`: Generate breadcrumb navigation items
---
Module 8: nuxt-site-config (Site Configuration)
Version: v4.0.7 | Downloads: 7.9M
Purpose
Centralized site configuration management for all SEO modules. Single source of truth for site-wide settings.
Configuration
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-site-config'],
site: {
url: 'https://example.com',
name: 'My Awesome Site',
description: 'Building amazing web experiences',
defaultLocale: 'en',
identity: {
type: 'Organization'
},
twitter: '@mysite',
trailingSlash: false
}
})Runtime Configuration
<script setup>
const siteConfig = useSiteConfig()
console.log(siteConfig.url) // https://example.com
console.log(siteConfig.name) // My Awesome Site
</script>APIs
- `useSiteConfig()`: Access site configuration
- `updateSiteConfig(config)`: Update site configuration
- `createSitePathResolver()`: Create path resolver with site URL
- `useNitroOrigin()`: Get origin URL server-side
---
Last Updated: 2025-11-27
Nuxt SEO v5 - Modules Overview
Detailed overview of all 8 Nuxt SEO modules.
---
Table of Contents
1. @nuxtjs/seo (Primary SEO Module) 2. @nuxtjs/robots (Robots.txt & Bot Detection) 3. @nuxtjs/sitemap (XML Sitemap Generation) 4. nuxt-og-image (Open Graph Image Generation) 5. nuxt-schema-org (Schema.org Structured Data) 6. nuxt-link-checker (Link Validation) 7. nuxt-seo-utils (SEO Utilities) 8. nuxt-site-config (Site Configuration) 9. Module Interactions 10. Version Compatibility 11. Installation Patterns
---
1. @nuxtjs/seo (Primary SEO Module)
Version: 5.1.0 | Downloads: 4.2M/month | Stars: 3,300
What It Does
- Installs all 8 SEO modules as a bundle
- Provides unified configuration via
siteobject - Integrates with Nuxt Content for automatic SEO
- Manages meta tags, titles, and descriptions
Key Features
- One-command installation of complete SEO stack
- Best-practice defaults out of the box
- Centralized configuration
- Zero-config for basic use cases
When to Use
- New projects needing complete SEO
- Simplifying multi-module installation
- Want best-practice defaults
- Need integrated Nuxt Content SEO
---
2. @nuxtjs/robots (Robots.txt & Bot Detection)
Version: 6.0.6 | Downloads: 8.7M
What It Does
- Generates robots.txt automatically
- Controls search engine crawling
- Detects bots server-side and client-side
- Manages page-level indexing rules
Key Features
- Automatic robots.txt generation
- User-agent specific rules
- Sitemap URL inclusion
- Clean URL parameters (Yandex)
- Bot detection with fingerprinting
- Page-level noindex/nofollow
- Nuxt I18n integration
- Route-based rules
APIs
useRobotsRule(rule)useBotDetection()getBotDetection(event)getPathRobotConfig(path)getSiteRobotConfig()
When to Use
- Block admin/private pages from crawlers
- Detect bots for analytics
- Configure per-search-engine rules
- Clean tracking parameters from URLs
- Multi-language robots.txt
---
3. @nuxtjs/sitemap (XML Sitemap Generation)
Version: 8.0.11 | Downloads: 10M
What It Does
- Auto-generates XML sitemaps from routes
- Supports dynamic content sources
- Creates sitemap indexes for large sites
- Includes images, videos, and news metadata
Key Features
- Automatic route detection
- Dynamic URL endpoints
- Multiple sitemap support
- Sitemap chunking (1000+ URLs)
- Image sitemap support
- Video sitemap support
- News sitemap support
- URL filtering
- Caching
- Nuxt Content integration
- i18n support
v5 New Features
definePageMetasitemap config- i18n multi-sitemap auto-expansion
- Debug production endpoint:
/__sitemap__/debug-production.json
APIs
When to Use
- Automatic sitemap generation
- E-commerce with thousands of products
- Blog with dynamic content
- Multi-language sites
- Media-rich websites
- Large sites needing chunking
---
4. nuxt-og-image (Open Graph Image Generation)
Version: 6.3.1 | Downloads: 3.7M
What It Does
- Generates Open Graph images dynamically
- Uses Vue templates for image design
- Creates social sharing previews
- Zero runtime overhead option
Key Features
- Two rendering engines (Satori, Chromium)
- Vue component templates
- Screenshot mode
- Custom fonts support
- Emoji support (Twemoji)
- Icon support (UnoCSS)
- JPEG and PNG formats
- Quality control
- Multi-language fonts
- Error page OG images
- Zero runtime mode
v5 New Features
- URL signing to prevent parameter tampering
- Prop whitelisting to prevent cache key DoS
- Strict mode, deprecated
htmlprop
Rendering Engines
- Satori: Fast, lightweight, HTML/CSS to image
- Chromium: Full browser, supports all features
APIs
defineOgImage(options)defineOgImageComponent(component, props)defineOgImageScreenshot(options)
When to Use
- Social media sharing previews
- Dynamic blog post images
- Product page thumbnails
- Event promotional images
- Error page sharing
- Multi-language og:images
---
5. nuxt-schema-org (Schema.org Structured Data)
Version: v6.0.4 | Downloads: 3.9M
What It Does
- Builds Schema.org JSON-LD graphs
- Enhances search results with rich snippets
- Provides knowledge panels
- Improves SEO visibility
Key Features
- Type-safe schema generation
- Organization identity
- Person identity
- Article/BlogPosting
- Product schemas
- Local business
- Event schemas
- FAQ pages
- Breadcrumbs
- Reviews & ratings
- Nuxt I18n support
APIs
useSchemaOrg(nodes)
Common Schema Types
- Organization
- Person
- Article/BlogPosting
- Product
- LocalBusiness
- Event
- FAQPage
- BreadcrumbList
- Review
- AggregateRating
When to Use
- Rich search results
- Knowledge panels
- Product listings
- Blog posts
- Local businesses
- Events
- FAQs
- Breadcrumb navigation
---
6. nuxt-link-checker (Link Validation)
Version: v5.0.6 | Downloads: 2.8M
What It Does
- Finds broken links automatically
- Validates internal and external links
- Detects redirect chains
- Reports link health
v5 New Features
- ESLint integration with
link-checker/valid-routeandlink-checker/valid-sitemap-linkrules - Scans Vue templates, TS/JS (
navigateTo,router.push), and Markdown links excludePagesconfig to skip link checking on specific pages
Key Features
- 404 detection
- Redirect chain identification
- Malformed URL detection
- Anchor link validation
- DevTools integration
- Build-time scanning
- Live inspections
- Exclusion patterns
- External link skipping
When to Use
- Development link validation
- Pre-deployment checks
- CI/CD pipeline integration
- Regular link audits
- Site migration validation
---
7. nuxt-seo-utils (SEO Utilities)
Version: v8.1.4 | Downloads: 2.2M
What It Does
- Provides SEO utility functions
- Manages canonical URLs
- Generates breadcrumbs
- Handles app icons
v5 New Features
useShareLinks()composable for social sharing (8 platforms + UTM tracking)nuxt-seo-utils iconsCLI for favicon generation from a single source image- Inline script/style minification (enabled by default)
- Brand new DevTools client with Identity tab
APIs
useBreadcrumbItems()useShareLinks(options)
When to Use
- Breadcrumb navigation
- Canonical URL enforcement
- App icon generation
- Template-based titles
- Route-specific SEO
---
8. nuxt-site-config (Site Configuration)
Version: v4.0.7 | Downloads: 7.9M
What It Does
- Centralizes site-wide configuration
- Provides runtime config access
- Manages multi-tenancy
- Integrates with i18n
v5 Breaking Changes
- Removed implicit site name inference (must explicitly set
site.name) - Removed server-side
useSiteConfig(event)— usegetSiteConfig(event)instead - Removed
getSiteIndexable()— use{ indexable } = getSiteConfig(event) - Removed
SiteConfigtype — useSiteConfigResolved - Removed legacy
siteUrl/siteName/siteDescriptionruntime config keys - Named priority constants:
SiteConfigPriority.runtime, etc.
Key Features
- Single source of truth
- Runtime configuration
- Multi-tenancy support
- Nuxt I18n integration
- Environment-based config
- Dynamic updates
- Path resolution
- Origin detection
APIs
useSiteConfig()(client-side, unchanged)getSiteConfig(event)(server-side, replacesuseSiteConfig(event))updateSiteConfig(config)createSitePathResolver()useNitroOrigin()
Configuration Options
url- Site URLname- Site namedescription- Site descriptiondefaultLocale- Default languageidentity- Organization/Persontwitter- Twitter handletrailingSlash- URL format
When to Use
- Site-wide SEO settings
- Multi-environment config
- Multi-tenancy setups
- Shared configuration
- Runtime config access
---
Module Interactions
How Modules Work Together
1. nuxt-site-config provides shared configuration 2. @nuxtjs/seo coordinates all modules 3. nuxt-robots references sitemap from nuxt-sitemap 4. nuxt-sitemap uses site.url from site-config 5. nuxt-og-image integrates with meta tags 6. nuxt-schema-org uses site identity 7. nuxt-seo-utils provides utilities for all 8. nuxt-link-checker validates generated sitemaps
Recommended Combinations
Blog Site:
- @nuxtjs/seo (or robots + sitemap + og-image + schema-org)
- Link checker for content validation
E-commerce:
- @nuxtjs/seo (all modules)
- Heavy focus on schema-org for products
- Multiple sitemaps for categories/products
Corporate Site:
- robots + sitemap + og-image + schema-org
- Organization schema for brand
- Link checker for maintenance
Multi-language:
- @nuxtjs/seo + @nuxtjs/i18n
- All modules with i18n integration
- Locale-specific sitemaps
---
Version Compatibility
All modules require:
- Nuxt >= 3.0.0
- Works with Nuxt 4.x
Package manager support:
- Bun (primary)
- npm (backup)
- pnpm (backup)
---
Installation Patterns
Full Stack
npx nuxt module add seoCustom Stack
npx nuxt module add robots
npx nuxt module add sitemap
npx nuxt module add nuxt-og-image
npx nuxt module add nuxt-schema-orgMinimal
npx nuxt module add robots
npx nuxt module add sitemap
npx nuxt module add nuxt-site-config---
Last Updated: 2026-03-30 Source: https://nuxtseo.com/llms-full.txt
Nuxt SEO - Nitro API Reference
Server-side APIs for customizing SEO behavior at runtime using Nitro hooks and composables.
---
Table of Contents
1. Sitemap Nitro Hooks 2. OG Image Nitro Hooks 3. Robots Nitro Composables 4. defineSitemapEventHandler 5. Common Recipes
---
Sitemap Nitro Hooks
Hooks for modifying sitemap output at runtime.
sitemap:input
Triggered when raw URLs are collected. Best for adding new URLs.
// server/plugins/sitemap.ts
import { defineNitroPlugin } from 'nitropack/runtime'
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('sitemap:input', async (ctx) => {
// Add string URL
ctx.urls.push('/foo')
// Add URL object
ctx.urls.push({
loc: '/bar',
changefreq: 'daily',
priority: 0.8,
})
})
})Type:
async (ctx: { urls: SitemapUrlInput[]; sitemapName: string }) => void | Promise<void>sitemap:resolved
Triggered after XML structure is generated. Best for modifying or removing entries.
// server/plugins/sitemap.ts
import { defineNitroPlugin } from 'nitropack/runtime'
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('sitemap:resolved', async (ctx) => {
// Add URL to specific sitemap
if (ctx.sitemapName === 'posts') {
ctx.urls.push({
loc: '/posts/my-post',
changefreq: 'daily',
priority: 0.8,
})
}
// Filter URLs
ctx.urls = ctx.urls.filter(url => !url.loc.includes('/private/'))
})
})Type:
async (ctx: { urls: ResolvedSitemapUrl[]; sitemapName: string }) => void | Promise<void>sitemap:index-resolved
Triggered when sitemap index is generated.
// server/plugins/sitemap.ts
import { defineNitroPlugin } from 'nitropack/runtime'
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('sitemap:index-resolved', async (ctx) => {
// Add external sitemap to index
ctx.sitemaps.push({
sitemap: 'https://mysite.com/my-sitemap.xml',
lastmod: new Date().toISOString(),
})
})
})Type:
async (ctx: { sitemaps: { sitemap: string, lastmod?: string }[] }) => void | Promise<void>sitemap:output
Triggered before sending to client. Access raw XML string.
// server/plugins/sitemap.ts
import { defineNitroPlugin } from 'nitropack/runtime'
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('sitemap:output', async (ctx) => {
// Append comment
ctx.sitemap = `${ctx.sitemap}\n<!-- Generated by Nuxt SEO -->`
// Add custom xmlns
ctx.sitemap = ctx.sitemap.replace(
'<urlset ',
'<urlset xmlns:mobile="http://www.baidu.com/schemas/sitemap-mobile/1/" '
)
})
})Type:
async (ctx: { sitemap: string; sitemapName: string }) => void | Promise<void>sitemap:sources
Triggered before resolving sources. Modify source list dynamically.
// server/plugins/sitemap.ts
import { defineNitroPlugin } from 'nitropack/runtime'
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('sitemap:sources', async (ctx) => {
// Add new source
ctx.sources.push('/api/dynamic-urls')
// Add auth headers from request
ctx.sources = ctx.sources.map(source => {
if (typeof source === 'object' && source.fetch) {
const [url, options = {}] = Array.isArray(source.fetch)
? source.fetch
: [source.fetch, {}]
const authHeader = ctx.event.node.req.headers.authorization
if (authHeader) {
options.headers = options.headers || {}
options.headers['Authorization'] = authHeader
}
source.fetch = [url, options]
}
return source
})
// Filter sources
ctx.sources = ctx.sources.filter(source => {
if (typeof source === 'string') {
return !source.includes('skip-this')
}
return true
})
})
})Type:
async (ctx: {
event: H3Event;
sitemapName: string;
sources: (SitemapSourceBase | SitemapSourceResolved)[]
}) => void | Promise<void>---
OG Image Nitro Hooks
Hooks for customizing OG image generation.
nuxt-og-image:context
Modify render context before image generation.
// server/plugins/ogImage.ts
import { defineNitroPlugin } from 'nitropack/runtime/plugin'
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('nuxt-og-image:context', async (ctx) => {
// Check path
if (!ctx.e.path.startsWith('/fancy-og-images/'))
return
// Modify props
ctx.options.props.isFancy = true
// Set custom cache key
ctx.key = 'fancy-og-images'
})
})Type:
async (ctx: OgImageRenderEventContext) => void | Promise<void>nuxt-og-image:satori:vnodes
Modify Satori virtual nodes before rendering.
// server/plugins/ogImage.ts
import { defineNitroPlugin } from 'nitropack/runtime/plugin'
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('nuxt-og-image:satori:vnodes', async (vnodes) => {
for (const child of vnodes.children) {
// Modify class
if (child.props?.class) {
child.props.class = child.props.class.replace('icon', '')
}
}
})
})Type:
async (vnodes: VNode[]) => void | Promise<void>OG Image Nuxt Hooks (Build-time)
nuxt-og-image:runtime-config
Modify OG Image config at build time.
// nuxt.config.ts
export default defineNuxtConfig({
hooks: {
'nuxt-og-image:runtime-config': (config) => {
config.colorPreference = 'dark'
}
}
})nuxt-og-image:components
Modify available OG Image components.
// nuxt.config.ts
export default defineNuxtConfig({
hooks: {
'nuxt-og-image:components': (ctx) => {
// Remove community components
ctx.components = ctx.components.filter(c => c.category !== 'community')
// Add custom component
const myComponentPath = resolve('./MyComponent.vue')
const myComponentContents = fsp.readFile(myComponentPath)
ctx.components.push({
hash: hash(myComponentContents),
pascalName: 'MyComponent',
kebabName: 'my-component',
path: nuxt.options.dev ? myComponentPath : undefined,
category: 'community',
credits: 'My Company',
})
}
}
})---
Robots Nitro Composables
Server-side composables for checking indexability.
getSiteRobotConfig()
Check if entire site is indexable.
// server/routes/og.png.ts
import { getSiteRobotConfig } from '#imports'
export default defineEventHandler((e) => {
const { indexable, hints } = getSiteRobotConfig(e)
if (!indexable) {
// Site is not indexable
// hints array explains why
console.log('Not indexable:', hints)
}
})Returns:
{
indexable: boolean // Whether site is indexable
hints: string[] // Reasons for indexability status
}getPathRobotConfig()
Check if specific path is indexable.
// server/plugins/strip-og-tags.ts
import { defineNitroPlugin, getPathRobotConfig } from '#imports'
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('render:html', async (ctx, { event }) => {
const { indexable, rule, debug } = getPathRobotConfig(event)
if (!indexable) {
// Strip OG tags for non-indexable pages
ctx.html = ctx.html.replace(/<meta property="og:.*?">/g, '')
}
})
})Options:
interface GetPathRobotConfigOptions {
userAgent?: string // Check for specific user agent
skipSiteIndexable?: boolean // Ignore site-wide config
path?: string // Override path to check
}Returns:
interface GetPathRobotResult {
rule: string // The robot rule (e.g., "noindex")
indexable: boolean // Whether path is indexable
debug?: { // Only in development
source: string // Source of the rule
line: string // Line number in source
}
}getBotDetection()
Access bot detection state in Nitro routes.
// server/api/protected.ts
import { getBotDetection } from '#imports'
export default defineEventHandler((e) => {
const botInfo = getBotDetection(e)
if (botInfo?.isBot) {
// Handle bot request differently
return { cached: true, data: getCachedData() }
}
return { cached: false, data: getFreshData() }
})---
defineSitemapEventHandler
Type-safe handler for creating sitemap URL endpoints.
Basic Usage
// server/api/__sitemap__/urls.ts
import { defineSitemapEventHandler } from '#imports'
import type { SitemapUrlInput } from '#sitemap/types'
export default defineSitemapEventHandler(() => {
return [
{
loc: '/about-us',
_sitemap: 'pages',
},
] satisfies SitemapUrlInput[]
})Async with API Fetch
// server/api/__sitemap__/urls.ts
import { defineSitemapEventHandler } from '#imports'
import type { SitemapUrl } from '#sitemap/types'
export default defineSitemapEventHandler(async () => {
const [posts, pages] = await Promise.all([
$fetch<{ slug: string }[]>('https://api.example.com/posts')
.then(posts => posts.map(p => ({
loc: `/blog/${p.slug}`,
_sitemap: 'posts',
} satisfies SitemapUrl))),
$fetch<{ path: string }[]>('https://api.example.com/pages')
.then(pages => pages.map(p => ({
loc: p.path,
_sitemap: 'pages',
} satisfies SitemapUrl))),
])
return [...posts, ...pages]
})WordPress Example
// server/api/__sitemap__/wordpress.ts
import { defineSitemapEventHandler } from '#imports'
export default defineSitemapEventHandler(async () => {
const posts = await $fetch('https://example.com/wp-json/wp/v2/posts')
return posts.map(post => ({
loc: `/blog/${post.slug}`, // NOT post.link
lastmod: post.modified,
changefreq: 'weekly',
priority: 0.7,
}))
})i18n Dynamic URLs
// server/api/__sitemap__/urls.ts
import { defineSitemapEventHandler } from '#imports'
import type { SitemapUrl } from '#sitemap/types'
export default defineSitemapEventHandler(async () => {
const config = useRuntimeConfig()
const baseUrl = config.public.siteUrl
const locales = config.public.i18n.locales.map(l => l.code)
const isoLocales = Object.fromEntries(
config.public.i18n.locales.map(l => ([l.code, l.iso]))
)
const apiQueries = locales.map(locale =>
$fetch(`${config.public.apiEndpoint}/sitemap/${locale}/products`)
)
const sitemaps = await Promise.all(apiQueries)
return sitemaps.flat().map(entry => ({
_sitemap: isoLocales[entry.locale],
loc: `${baseUrl}/${entry.locale}/product/${entry.url}`,
alternatives: entry.alternates?.map(alt => ({
hreflang: isoLocales[alt.locale],
href: `${baseUrl}/${alt.locale}/product/${alt.url}`
}))
} satisfies SitemapUrl))
})---
Common Recipes
Filter Videos by Host
// server/plugins/sitemap.ts
import { defineNitroPlugin } from 'nitropack/runtime'
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('sitemap:resolved', (ctx) => {
ctx.urls.map((url) => {
if (url.videos?.length) {
url.videos = url.videos.filter((video) => {
if (video.content_loc) {
const parsedUrl = new URL(video.content_loc)
return parsedUrl.host.startsWith('www.youtube.com')
}
return false
})
}
return url
})
})
})Extract YouTube Videos from Prerender
// nuxt.config.ts
import type { ResolvedSitemapUrl } from '#sitemap/types'
export default defineNuxtConfig({
modules: [
(_, nuxt) => {
nuxt.hooks.hook('nitro:init', async (nitro) => {
nitro.hooks.hook('prerender:generate', async (route) => {
const html = route.contents
const matches = html.match(/<iframe.*?youtube.com\/embed\/(.*?)".*?<\/iframe>/g)
if (matches) {
const sitemap = route._sitemap || {} as ResolvedSitemapUrl
sitemap.videos = sitemap.videos || []
for (const match of matches) {
const videoId = match.match(/youtube.com\/embed\/(.*?)"/)[1]
sitemap.videos.push({
title: 'YouTube Video',
description: 'A video from YouTube',
content_loc: `https://www.youtube.com/watch?v=${videoId}`,
thumbnail_loc: `https://img.youtube.com/vi/${videoId}/0.jpg`,
})
}
route._sitemap = sitemap
}
})
})
},
],
})Strip OG Tags for Non-Indexable Pages
// server/plugins/strip-og-tags.ts
import { defineNitroPlugin, getPathRobotConfig } from '#imports'
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('render:html', async (ctx, { event }) => {
const { indexable } = getPathRobotConfig(event)
if (!indexable) {
ctx.html = ctx.html.replace(/<meta property="og:.*?">/g, '')
}
})
})Disable OG Image Generation for Non-Indexable Sites
// server/routes/og.png.ts
import { getSiteRobotConfig } from '#imports'
export default defineEventHandler((e) => {
const { indexable } = getSiteRobotConfig(e)
if (!indexable) {
// Return placeholder or 404
throw createError({
statusCode: 404,
message: 'OG Image not available'
})
}
// Generate OG image normally
})Add Custom xmlns to Sitemap
// server/plugins/sitemap.ts
import { defineNitroPlugin } from 'nitropack/runtime'
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('sitemap:output', async (ctx) => {
// Baidu mobile sitemap
ctx.sitemap = ctx.sitemap.replace(
'<urlset ',
'<urlset xmlns:mobile="http://www.baidu.com/schemas/sitemap-mobile/1/" '
)
})
})---
Official Documentation
- Sitemap Nitro Hooks: https://nuxtseo.com/docs/sitemap/nitro-api/nitro-hooks
- OG Image Nitro Hooks: https://nuxtseo.com/docs/og-image/nitro-api/nitro-hooks
- Robots Nitro API: https://nuxtseo.com/docs/robots/nitro-api/get-site-robot-config
- GitHub: https://github.com/harlan-zw