Skip to content

Nuxt SEO and Meta Tags — Search Engine Optimization in Nuxt 3

DodaTech Updated 2026-06-28 4 min read

Learn how to optimize Nuxt 3 for search engines using useHead, definePageMeta, Open Graph tags, and structured data for better rankings.

In this lesson, you'll understand how to manage SEO in Nuxt 3, create reusable SEO composables, and implement JSON-LD structured data.

What You'll Learn

How to use useHead for page metadata, definePageMeta for static SEO, create a reusable SEO composable, and add structured data.

Why It Matters

SEO directly impacts organic traffic. Nuxt 3 provides excellent head management with SSR support, ensuring search engines see complete meta tags.

flowchart LR
    A[Nuxt SEO] --> B[useHead Dynamic]
    A --> C[definePageMeta Static]
    A --> D[Structured Data]
    B --> E[Page Title]
    B --> F[Meta Tags]
    B --> G[OG Tags]
    D --> H[JSON-LD Schema]
    style A fill:#00dc82,color:#fff

Basic useHead

<script setup>
useHead({
  title: 'About Us',
  titleTemplate: '%s | DodaTech Tutorials',
  meta: [
    { name: 'description', content: 'Learn about DodaTech and our mission.' },
    { name: 'author', content: 'DodaTech' }
  ]
});
</script>

Output: The page title becomes "About Us | DodaTech Tutorials". Search engines see the meta description.

definePageMeta

Static SEO data per page:

<script setup>
definePageMeta({
  title: 'Blog',
  description: 'Read the latest tutorials and articles.',
  ogImage: '/images/blog-og.jpg'
});
</script>

definePageMeta is compile-time and can't use reactive values. For dynamic SEO, use useHead.

Reusable SEO Composable

// composables/useSEO.ts
export const useSEO = (options: {
  title: string;
  description: string;
  image?: string;
  path?: string;
  type?: 'article' | 'website';
  publishedTime?: string;
  tags?: string[];
}) => {
  const { siteUrl, siteName } = useRuntimeConfig().public;
  const route = useRoute();

  const url = `${siteUrl}${options.path || route.path}`;
  const image = options.image ? `${siteUrl}${options.image}` : `${siteUrl}/og-default.jpg`;

  useHead({
    title: options.title,
    titleTemplate: `%s | ${siteName}`,
    meta: [
      { name: 'description', content: options.description },

      // Open Graph
      { property: 'og:title', content: options.title },
      { property: 'og:description', content: options.description },
      { property: 'og:url', content: url },
      { property: 'og:type', content: options.type || 'website' },
      { property: 'og:image', content: image },
      { property: 'og:site_name', content: siteName },

      // Twitter
      { name: 'twitter:card', content: 'summary_large_image' },
      { name: 'twitter:title', content: options.title },
      { name: 'twitter:description', content: options.description },
      { name: 'twitter:image', content: image },

      // Article-specific
      ...(options.publishedTime ? [{ property: 'article:published_time', content: options.publishedTime }] : []),
      ...(options.tags ? options.tags.map(tag => ({ property: 'article:tag', content: tag })) : [])
    ],
    link: [
      { rel: 'canonical', href: url }
    ]
  });
};

Usage:

<script setup>
useSEO({
  title: 'Getting Started with Nuxt 3',
  description: 'Learn how to build modern web applications with Nuxt 3 framework.',
  path: '/blog/getting-started-nuxt',
  type: 'article',
  publishedTime: '2026-06-28',
  tags: ['nuxt', 'vue', 'tutorial']
});
</script>

Structured Data (JSON-LD)

<script setup>
const { siteUrl } = useRuntimeConfig().public;
const route = useRoute();

const articleSchema = {
  '@context': 'https://schema.org',
  '@type': 'Article',
  headline: 'Getting Started with Nuxt 3',
  description: 'Learn how to build modern web applications with Nuxt 3.',
  image: `${siteUrl}/images/nuxt-guide.jpg`,
  author: {
    '@type': 'Person',
    name: 'DodaTech'
  },
  datePublished: '2026-06-28',
  dateModified: '2026-06-28'
};

useHead({
  script: [
    {
      type: 'application/ld+json',
      children: JSON.stringify(articleSchema)
    }
  ]
});
</script>

Output: Search engines see the structured data and may display rich results with author, date, and image.

Nuxt Config SEO

Set global SEO defaults:

// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      titleTemplate: '%s | DodaTech',
      meta: [
        { name: 'viewport', content: 'width=device-width, initial-scale=1' }
      ],
      link: [
        { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
      ],
      htmlAttrs: {
        lang: 'en'
      }
    }
  }
});

Common Mistakes

  1. Not setting titleTemplate: Without it, all pages show the same title. Use titleTemplate: '%s | Site Name'.
  2. Using relative URLs for OG images: Open Graph requires absolute URLs. Prepend the siteUrl.
  3. Duplicate canonical URLs: Every page should have a canonical URL to prevent duplicate content issues.
  4. Not generating a sitemap: Install @nuxtjs/sitemap module for automatic sitemap generation.
  5. Missing lang attribute: Set htmlAttrs: { lang: 'en' } for Accessibility and SEO.

Practice Questions

  1. What is the difference between useHead and definePageMeta? Answer: useHead is for dynamic, reactive head management. definePageMeta is compile-time static metadata.

  2. How do you set a title template that appends the site name? Answer: Use titleTemplate: '%s | Site Name' in useHead. The %s is replaced by the page title.

  3. Why should OG image URLs be absolute? Answer: Social media crawlers fetch images from absolute URLs. Relative URLs don't resolve correctly from the crawler's perspective.

  4. What module generates a sitemap in Nuxt 3? Answer: @nuxtjs/sitemap. Install and configure it in nuxt.config.ts.

Challenge

Create a reusable useSEO composable that accepts: title, description, image, path, type (article/website), publishedTime, tags, and custom meta. Generate JSON-LD for Article and FAQPage schemas.

Mini Project

Implement full SEO across a Nuxt 3 blog: global defaults in nuxt.config.ts, static SEO on pages with definePageMeta, dynamic SEO on blog posts with useHead, JSON-LD schema per post, Open Graph images, Twitter cards, and sitemap generation.

FAQ

Does Nuxt 3 support SSR for meta tags?

: Yes. Meta tags are rendered server-side, so search engines see the complete head content.

How do I add Google Analytics?

: Use the @nuxtjs/google-analytics module or add the script manually with useHead.

Can I conditionally set meta tags?

: Yes. useHead accepts reactive refs and computed values for dynamic meta tags.

How do I test my SEO implementation?

: Use Google's Rich Results Test, Facebook Sharing Debugger, and Lighthouse SEO audit.

What's Next

Learn about Nuxt Static Generation to generate a fully static site with nuxt generate.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro