Nuxt 3 SEO & Meta Management

Nuxt 3 renders universally by default, which means it already does the hardest part of JavaScript SEO — putting content and metadata in the HTTP response — without extra configuration. The work that remains is choosing the right rendering mode per route and setting metadata cleanly per page. This guide maps Nuxt’s SEO surface and sits within framework-specific SEO implementations, applying the rendering-strategy decisions in Nuxt’s idiom.

Prerequisites

  • Nuxt 3 with the default universal (SSR) rendering, or Nitro configured for your hosting target.
  • A clear per-route rendering intent (SSR, prerender/SSG, or ISR).
  • Page components using useSeoMeta / useHead, or definePageMeta for route-level config.
  • Search Console access to verify rendered metadata.

How it breaks

The typical Nuxt SEO regression is disabling SSR globally (ssr: false) to fix a build issue, turning the whole app into a client-rendered SPA — so every route reverts to the empty-shell failure Nuxt was protecting you from. A subtler one is setting metadata in onMounted, which runs only on the client and never makes it into the server response.

Nuxt 3 SEO surface feeding the server-rendered head Route rules, useSeoMeta, and useHead all feed into the universally rendered document head delivered in the HTTP response. routeRules (mode) useSeoMeta (tags) useHead (JSON-LD) Universal render server head crawler
Route rules and the head composables all resolve during the universal render, so the crawler receives complete metadata.

Step-by-step fix

  1. Keep SSR on; set the mode per route with route rules.

    // nuxt.config.ts
    export default defineNuxtConfig({
      routeRules: {
        '/':         { prerender: true }, // SSG
        '/blog/**':  { isr: 3600 },       // ISR
        '/shop/**':  { ssr: true },       // SSR
      },
    });
  2. Set standard metadata with useSeoMeta. It is typed and runs during SSR.

    <script setup>
    const { data: post } = await useFetch(`/api/posts/${useRoute().params.slug}`);
    useSeoMeta({
      title: () => post.value.title,
      description: () => post.value.summary,
      ogTitle: () => post.value.title,
      ogImage: () => post.value.ogImage,
    });
    </script>
  3. Add structured data with useHead. Use it for JSON-LD and link tags — detailed in dynamic meta tags with useHead in Nuxt 3.

  4. Never set SEO metadata in onMounted — it runs client-only and misses the server response.

Gotchas & edge cases

  • Global ssr: false. Turns the app into a CSR SPA; scope client-only rendering to route rules instead.
  • await before useSeoMeta. Ensure data is awaited so the title is resolved during SSR, not after.
  • Duplicate tags from manual useHead. Prefer useSeoMeta for standard tags to avoid conflicting entries.
  • Canonical on parameterized routes. Set a clean canonical via useHead for filtered or paginated routes — see canonical URL management.

Validation checklist

Performance & crawl-budget notes

Because Nuxt renders metadata during SSR, routes index in the first wave and skip the render queue, preserving crawl budget. Route rules let you reserve full SSR for genuinely dynamic routes and prerender the rest, minimizing server cost while keeping everything crawlable.

Go deeper

Frequently Asked Questions

Is Nuxt 3 good for SEO by default? Yes. Nuxt 3 renders universally by default, so routes ship server-rendered HTML with metadata in the response. The SEO work is mostly choosing the right per-route rendering mode with route rules and setting per-page metadata with useSeoMeta or useHead.

Should I use useHead or useSeoMeta in Nuxt 3? Use useSeoMeta for standard SEO and social tags — it is typed and maps directly to title, description, and og: properties. Use useHead for anything useSeoMeta does not cover, such as link tags, scripts, or JSON-LD structured data.

← Back to Framework-Specific SEO Implementations