Nuxt SEO: Everything You Need for Ranking in 2026
A complete technical SEO guide for Nuxt applications — meta tags, structured data, sitemaps, Core Web Vitals, and the @nuxtjs/seo module that handles it all.

James Ross Jr.
Strategic Systems Architect & Enterprise Software Developer
SEO is where a lot of frontend frameworks fall short, and where Nuxt genuinely shines. The combination of server-side rendering, a well-designed head management API, and a growing ecosystem of SEO-focused modules means you can build a technically excellent site for search without fighting your framework.
But having the tools available and using them correctly are different things. I have audited dozens of Nuxt sites that had all the right modules installed and still had preventable SEO problems. This guide is about using the tools correctly.
The Foundation: Proper Meta Tags
Every page needs a unique, descriptive title and a compelling meta description. In Nuxt, you set these with useSeoMeta:
<script setup lang="ts">
useSeoMeta({
title: 'Product Name — Your Brand',
description: 'Your unique, compelling 150-160 character description goes here. Write for humans, not bots.',
ogTitle: 'Product Name — Your Brand',
ogDescription: 'Open Graph description for social sharing.',
ogImage: 'https://yourdomain.com/og/product-name.png',
ogUrl: 'https://yourdomain.com/products/product-name',
twitterCard: 'summary_large_image',
})
</script>
The useSeoMeta composable is typed — it knows which meta tags accept which values and will warn you about incorrect usage. Use it on every page, not just the homepage.
For dynamic pages like blog posts or product detail pages, compute the values from your data:
<script setup lang="ts">
const { data: post } = await useAsyncData('post', () =>
queryCollection('blog').path(useRoute().path).first()
)
useSeoMeta({
title: () => `${post.value?.title} — James Ross Jr.`,
description: () => post.value?.description,
ogTitle: () => post.value?.title,
ogImage: () => `https://jamesrossjr.com/og/${post.value?.slug}.png`,
})
</script>
The function form ensures the values update reactively when your data loads.
The @nuxtjs/seo Module
Rather than manually wiring every SEO concern, the @nuxtjs/seo module consolidates the most important tools into one:
npx nuxi module add seo
This brings in robots.txt generation, sitemap generation, Open Graph tags, Twitter card support, and schema.org structured data — all configured from a single place in nuxt.config.ts:
seo: {
redirectToCanonicalSiteUrl: true,
},
site: {
url: 'https://jamesrossjr.com',
name: 'James Ross Jr.',
description: 'Strategic Systems Architect & Enterprise Software Developer',
defaultLocale: 'en',
},
Sitemaps That Actually Help
A proper sitemap tells search engines what pages exist, when they were last modified, and their relative importance. The @nuxtjs/sitemap module generates this automatically:
// nuxt.config.ts
sitemap: {
sources: ['/api/__sitemap__/urls'],
excludeAppSources: ['/api/auth/**'],
defaults: {
changefreq: 'weekly',
priority: 0.8,
lastmod: new Date(),
},
urls: [
{ loc: '/', priority: 1.0, changefreq: 'daily' },
{ loc: '/about', priority: 0.9 },
{ loc: '/contact', priority: 0.7 },
],
},
For content-driven sites, the module automatically discovers routes from your pages/ directory and Nuxt Content collections. You should not need to manually list every blog post.
Submit your sitemap to Google Search Console and Bing Webmaster Tools after launch. Verify it is accessible at /sitemap.xml and contains all your important pages.
Structured Data With Schema.org
Structured data is how you communicate machine-readable context to search engines. It enables rich results — star ratings in search results, FAQ dropdowns, article cards with author images.
For blog articles, use the Article schema:
<script setup lang="ts">
useSchemaOrg([
defineArticle({
headline: post.value.title,
description: post.value.description,
datePublished: post.value.date,
dateModified: post.value.updatedAt ?? post.value.date,
author: {
'@type': 'Person',
name: 'James Ross Jr.',
url: 'https://jamesrossjr.com',
},
image: `https://jamesrossjr.com/og/${post.value.slug}.png`,
}),
])
</script>
For a personal site, add Person schema to the homepage:
useSchemaOrg([
definePerson({
name: 'James Ross Jr.',
description: 'Strategic Systems Architect & Enterprise Software Developer',
url: 'https://jamesrossjr.com',
sameAs: [
'https://linkedin.com/in/jamesrossjr',
'https://github.com/jamesrossjr',
],
}),
])
Validate your structured data with Google's Rich Results Test before considering it done.
Core Web Vitals: The Rankings Signal
Since Google's page experience update, Core Web Vitals are a direct ranking signal. The three metrics are Largest Contentful Paint (LCP), Cumulative Layout Shift (CLS), and Interaction to Next Paint (INP).
LCP measures how quickly the main content loads. The target is under 2.5 seconds. Nuxt SSR helps here because the server sends HTML immediately. But you also need:
- Images served in modern formats (WebP, AVIF)
loading="eager"andfetchpriority="high"on your above-the-fold image- A CDN serving assets close to users
<!-- The main hero image should be eager, not lazy -->
<NuxtImg
src="/hero.jpg"
alt="Hero image description"
width="1200"
height="630"
loading="eager"
fetchpriority="high"
format="webp"
/>
CLS measures unexpected layout shifts. The most common cause is images without declared dimensions. Always provide width and height attributes on images. Use skeleton loaders for async content to reserve space.
<!-- Without width/height, the image causes layout shift -->
<img src="/product.jpg" width="400" height="300" alt="Product image" />
<!-- For async content, reserve space with a skeleton -->
<div v-if="loading" class="h-64 bg-gray-100 animate-pulse rounded" />
<ProductCard v-else :product="product" />
INP measures responsiveness to user input. Heavy JavaScript on the main thread is the main culprit. Keep your JavaScript bundles lean, defer non-critical scripts, and avoid long-running synchronous operations.
Robots.txt and Crawl Control
A proper robots.txt file tells crawlers what to index:
// nuxt.config.ts
robots: {
disallow: ['/api/', '/admin/', '/_nuxt/'],
allow: '/',
},
Block routes you do not want indexed: API endpoints, admin panels, staging environments, internal search result pages with URL parameters.
For pages that should exist but not be indexed (thank-you pages, confirmation pages, paginated pages after page 2), use the robots meta tag:
<script setup lang="ts">
useSeoMeta({
robots: 'noindex, follow',
})
</script>
Canonical URLs: Preventing Duplicate Content
Duplicate content dilutes your SEO signal. Canonical tags tell search engines which version of a page is the authoritative one:
// nuxt.config.ts
// @nuxtjs/seo handles this with redirectToCanonicalSiteUrl
// But you can also set it manually per page:
useSeoMeta({
canonical: `https://jamesrossjr.com${route.path}`,
})
Common duplication sources: HTTP vs HTTPS, www vs non-www, trailing slash vs no trailing slash. Configure your hosting to redirect one canonical form and set redirectToCanonicalSiteUrl: true in your SEO module configuration.
Internationalized SEO
If your site targets multiple languages, hreflang tags are essential. They tell search engines which language version to show to which users:
<script setup lang="ts">
useHead({
link: [
{ rel: 'alternate', hreflang: 'en', href: 'https://yourdomain.com/en/page' },
{ rel: 'alternate', hreflang: 'es', href: 'https://yourdomain.com/es/page' },
{ rel: 'alternate', hreflang: 'x-default', href: 'https://yourdomain.com/en/page' },
],
})
</script>
The @nuxtjs/i18n module handles this automatically when configured correctly.
Measuring and Monitoring
Set up Google Search Console on day one, not after you are already concerned about rankings. The data it provides about impressions, clicks, and crawl errors is irreplaceable.
Track Core Web Vitals in production with the web-vitals library. CrUX (Chrome User Experience Report) data in Search Console shows real-user metrics, not just lab data. They often tell different stories.
Check your structured data monthly with Google's Rich Results Test. A module update or template change can accidentally break schema output without any visible error.
SEO is a discipline, not a task you complete. The technical foundation — correct meta tags, valid structured data, fast Core Web Vitals, clean sitemaps — needs to be in place before content strategy can compound. Get the foundation right first.
Want a technical SEO audit of your Nuxt application or help setting up the right SEO infrastructure from the start? Book a call: calendly.com/jamesrossjr.