Skip to content
ExponaBLOG
VUE.JS & NUXT · DAY 039 / 100

Nuxt SEO checklist for ranking pages: a practical guide

Everything a Nuxt site needs to rank: meta tags, sitemaps, structured data and the right rendering mode.

Expona Admin6 min read

Why SEO matters for Nuxt applications

Search engines need to understand your content, and Nuxt makes that easier than most frameworks—but only if you do it right. Getting SEO wrong costs you visibility, traffic, and revenue. This Nuxt SEO checklist for ranking pages: a practical guide walks through everything your site needs to rank: meta tags, sitemaps, structured data and the right rendering mode. We've seen too many teams launch Nuxt apps that look great but vanish from search results because they skipped one of these fundamentals.

useSeoMeta — Typed meta tags per page

Every page needs a unique title, description, and Open Graph tags. Nuxt's useSeoMeta() composable handles this cleanly and safely—it's typed, composable-friendly, and works on both server and client.

Start with the basics: title and description. These are the most important signals to search engines and what users see in search results.

typescript
// pages/products/[id].vue
import { useSeoMeta } from '#app'

export default definePageMeta({
  layout: 'product',
})

export default defineComponent({
  setup() {
    const route = useRoute()
    const product = ref(null)

    onMounted(async () => {
      const id = route.params.id
      product.value = await $fetch(`/api/products/${id}`)
    })

    useSeoMeta({
      title: product.value?.name || 'Product',
      description: product.value?.summary || '',
      ogTitle: product.value?.name,
      ogDescription: product.value?.summary,
      ogImage: product.value?.image,
      ogType: 'product',
    })

    return { product }
  },
})

The key insight: titles and descriptions must be unique per page. Search engines penalize duplicate meta tags. If you have 10,000 product pages, you need 10,000 unique titles and descriptions. That means fetching them from your backend or database, not hardcoding.

For e-commerce sites, include the product's main image in ogImage. For SaaS landing pages, use your branded hero image. For blog posts, use the article thumbnail. Social platforms (LinkedIn, Twitter, Facebook) use these tags when people share your links—bad og:image means lower click-through rates.

Also set the canonical tag if you have multiple URLs for the same content (rare in Nuxt, but matters for paginated lists):

typescript
useSeoMeta({
  title: 'Search results',
  description: 'Find products matching your query',
  canonical: `https://example.com/search?q=${query.value}&page=1`,
})

Sitemaps and robots — Nuxt SEO modules generate them

Google needs to discover all your pages. A sitemap is an XML file listing every URL on your site, and robots.txt tells search engines which parts to crawl.

The @nuxtjs/sitemap module generates both automatically:

typescript
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/sitemap'],

  sitemap: {
    hostname: 'https://example.com',
    routes: async () => {
      // For dynamic routes, fetch from your API
      const products = await $fetch('https://api.example.com/products')
      return products.map(p => `/products/${p.id}`)
    },
    sitemapSize: 50000, // Split into multiple files if needed
    gzip: true,
  },
})

This generates sitemap.xml and sitemap_index.xml automatically. Submit the sitemap URL (https://example.com/sitemap.xml) to Google Search Console. Google crawls it and discovers your pages 3-5x faster.

For robots.txt, add it to your public/ folder:

typescript
// public/robots.txt
User-agent: *
Allow: /
Disallow: /admin/
Disallow: /private/
Sitemap: https://example.com/sitemap.xml

The Disallow rules prevent search engines from crawling your admin panel or user accounts. This saves crawl budget—search engines allocate a limited number of requests per site per day, so blocking junk pages means more crawls on important ones.

Structured data — JSON-LD with schema modules

Structured data (JSON-LD) tells search engines what your page is about in a machine-readable format. Google uses this to display rich snippets, product ratings, breadcrumbs, and other enhanced results that drive higher click-through rates.

The @nuxtjs/schema-org module makes this painless:

typescript
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/schema-org'],

  schemaOrg: {
    host: 'https://example.com',
  },
})

Then in your product page:

typescript
// pages/products/[id].vue
import { useSchemaOrg, defineProduct } from '#schema-org'

export default defineComponent({
  setup() {
    const route = useRoute()
    const product = ref(null)

    onMounted(async () => {
      product.value = await $fetch(`/api/products/${route.params.id}`)
    })

    useSchemaOrg([
      defineProduct({
        name: product.value?.name,
        description: product.value?.description,
        image: product.value?.image,
        brand: {
          name: 'Your Brand',
        },
        offers: {
          price: product.value?.price,
          priceCurrency: 'USD',
          availability: product.value?.inStock ? 'InStock' : 'OutOfStock',
        },
        aggregateRating: product.value?.rating ? {
          ratingValue: product.value.rating,
          reviewCount: product.value.reviewCount,
        } : undefined,
      }),
    ])

    return { product }
  },
})

Google parses this and displays star ratings, prices, and availability directly in search results. Products with rich snippets get 20-30% more clicks than plain blue links.

For blog posts, use defineArticle(). For organization homepage, use defineOrganization(). For breadcrumb navigation, use defineBreadcrumb(). Each one improves your SERP appearance.

Rendering — SSR or prerender pages that need to rank

This is the make-or-break decision. Nuxt supports three rendering modes:

  • SSR (Server-Side Rendering): Server renders HTML on every request. Search engines get fully-rendered pages instantly. Slightly higher server cost, but pages always current.
  • Prerendering: Build-time rendering. Nuxt renders all pages to static HTML at build time. Zero server cost, instant TTFB, but pages are stale until rebuild.
  • CSR (Client-Side Rendering): Browser renders HTML via JavaScript. Search engines struggle here; many bots don't wait for JS to run. Avoid unless you're building an internal tool.

For SEO, SSR is the safest bet. It guarantees search engines always see current content:

typescript
// nuxt.config.ts
export default defineNuxtConfig({
  ssr: true, // Default, but be explicit
})

If your site is mostly static (blog, docs, marketing pages), prerendering is faster and cheaper:

typescript
// nuxt.config.ts
export default defineNuxtConfig({
  ssr: true,
  prerender: {
    crawlLinks: true,
    routes: ['/sitemap.xml'],
    ignore: ['/admin'],
  },
})

Set crawlLinks: true and Nuxt crawls all internal links during build, auto-discovering your entire site. For e-commerce or travel sites with millions of pages, manually list critical routes and let Nuxt handle the rest via your sitemap.

One more thing: Core Web Vitals matter. Google ranks faster sites higher. Prerendered sites usually win here because they're pure static HTML. If you go SSR, optimize your API responses and database queries—slow servers mean slow pages, which means lower rankings.

Common mistakes

Mistake 1: Hardcoded meta tags. Setting the same title on every page or using placeholder descriptions tanks your ranking. Every page needs unique, relevant meta tags pulled from your database or CMS.

Mistake 2: Forgetting the sitemap. Even if Google can crawl your site organically, a sitemap speeds discovery by 3-5x. Submitting to Search Console takes five minutes and pays off for months.

Mistake 3: Using CSR for public pages. Client-side rendering works for dashboards and apps behind login, but public marketing and content pages need SSR. Search engines don't wait for JavaScript to load.

Mistake 4: No structured data. Rich snippets aren't required to rank, but they dramatically improve CTR. A few hours implementing JSON-LD across your key pages yields real traffic gains.

Mistake 5: Shipping with ssr: false then wondering why you rank nowhere. We've seen this. Always verify ssr: true is set in production. Use Lighthouse in Google Search Console to test actual rendering.

Key takeaways

  • Use useSeoMeta() to set unique, database-driven title and description on every page.
  • Generate sitemaps with @nuxtjs/sitemap and submit to Google Search Console.
  • Add JSON-LD structured data with @nuxtjs/schema-org to earn rich snippets and higher CTR.
  • Choose SSR for dynamic content, prerender for mostly-static sites. Avoid CSR for public pages.
  • Test rendering with Lighthouse in Search Console to verify search engines see your content.
  • Focus on Core Web Vitals—fast sites rank higher than slow ones.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles