SEO Optimization: useHead + useSeoMeta
Charlie's MegaShop has a million product pages, but Google has only indexed 2,000 of them. When Alice searches for a product name, she can't find any pages from MegaShop. Bob checked and found that the product pages lack titles, meta descriptions, Open Graph tags, and structured data. Nuxt 3's SEO toolchain can solve all of these issues.
1. What You'll Learn
- useHead(): Dynamically manage title, meta, link, script, and style tags
- useSeoMeta(): Structured SEO metadata (OG/Twitter/description)
- Nuxt 3's Built-in SEO Defaults and Override Mechanisms
- Sitemap and robots.txt: @nuxtjs/sitemap module
- MegaShop Product Detail Page Dynamic SEO + JSON-LD + Automatic Sitemap Generation
2. A True Story of an Architect
(1) Pain Point: Out of a million pages, only 2,000 are indexed by Google
Charlie checked MegaShop using Google Search Console and found that out of 1 million product pages, only 2,000 had been indexed. Bob's analysis revealed that all product pages had the title "MegaShop," empty meta descriptions, and missing Open Graph tags—meaning that when shared on social media, only the website name appeared, with no product information.
(2) Solutions for the Nuxt SEO Toolchain
Nuxt 3's useHead and useSeoMeta allow each product page to have its own SEO metadata:
// pages/products/[id].vue
useSeoMeta({
title: product.value?.name,
ogTitle: product.value?.name,
description: `Buy ${product.value?.name} for $${product.value?.price} USD`,
ogImage: product.value?.image
})
(3) Results: Google indexing rate soared 40-fold
After three months, Google's index surged from 2,000 to 800,000, and organic search traffic increased by 340%.
3. SEO Toolchain Architecture
(1) An Overview of the Nuxt 3 SEO Toolchain
graph TB
A[Nuxt SEO Toolkit] --> B[useHead]
A --> C[useSeoMeta]
A --> D[JSON-LD]
A --> E[@nuxtjs/sitemap]
A --> F[robots.txt]
B --> B1[title / meta / link]
C --> C1[OG / Twitter Card]
D --> D1[Product / Breadcrumb]
E --> E1[Dynamic URL Generation]
F --> F1[Crawler Access Control]
```
---
## 4. Dynamic Management of useHead
### (1) What can useHead manage?
| Tag Type | Attribute | SEO Impact |
|:---------|:-----|:---------|
| title | Page Title | 🔴 High (Ranking Signal) |
| meta name="description" | Page Description | 🔴 High (Click-Through Rate) |
| meta property="og:*" | Open Graph | 🟡 Medium (Social Sharing) |
| meta name="twitter:*" | Twitter Card | 🟡 Medium (Social Sharing) |
| link rel="canonical" | Canonical URL | 🔴 High (duplicate removal) |
| link rel="alternate" | Multilingual URL | 🟡 Chinese (i18n SEO) |
| script type="application/ld+json" | JSON-LD | 🟡 Supported (Rich Snippets) |
### (1) ▶ Example: Basic Usage of useHead
```typescript
// In any component or page
useHead({
title: 'Premium Headphones - MegaShop',
meta: [
{ name: 'description', content: 'Buy Premium Headphones for $299.99 USD. Free shipping.' },
{ name: 'keywords', content: 'headphones, wireless, premium, audio' }
],
link: [
{ rel: 'canonical', href: 'https://megashop.com/products/123' }
]
})
Output:
// Execution Successful
(2) ▶ Example: Dynamic title (computed, reactive)
<script setup lang="ts">
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)
// Dynamic title that updates when product loads
useHead({
title: computed(() => product.value
? `${product.value.name} - MegaShop`
: 'Loading... - MegaShop'
)
})
</script>
Output:
// Execution Successful
(3) ▶ Example: Global Default Configuration
// nuxt.config.ts
export default defineNuxtConfig({
app: {
head: {
title: 'MegaShop - Premium E-Commerce',
titleTemplate: '%s | MegaShop',
meta: [
{ name: 'description', content: 'Over 1 million products shipped worldwide' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ charset: 'utf-8' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
}
}
})
Output:
// Execution Successful
4. useSeoMeta Structured SEO
(1) Comparison of useSeoMeta and useHead
| Dimension | useHead | useSeoMeta |
|---|---|---|
| Targeting | General Head Management | SEO-Specific Shortcuts |
| API Style | Array Object | Key-Value Pairs |
| OG/Twitter | Requires manual meta tag entry | Done in one line |
| Type Safety | ⚠️ Check documentation | ✅ Complete type hints |
| Duplicate Removal | ⚠️ Manual Management | ✅ Automatic Duplicate Removal |
(1) ▶ Example: Product Detail Page SEO
<!-- pages/products/[id].vue -->
<script setup lang="ts">
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)
// Structured SEO metadata - type-safe
useSeoMeta({
title: () => `${product.value?.name} - Buy Online`,
ogTitle: () => `${product.value?.name} - MegaShop`,
description: () => `Buy ${product.value?.name} for $${product.value?.price} USD. ${product.value?.reviewCount} reviews, ${product.value?.rating} stars. Free shipping worldwide.`,
ogDescription: () => `Premium ${product.value?.name} - $${product.value?.price} USD`,
ogImage: () => product.value?.image || '/images/og-default.jpg',
ogUrl: () => `https://megashop.com/products/${route.params.id}`,
ogType: 'product',
twitterCard: 'summary_large_image',
twitterTitle: () => `${product.value?.name} - MegaShop`,
twitterImage: () => product.value?.image || '/images/og-default.jpg'
})
</script>
Output:
// Execution Successful
(2) ▶ Example: Homepage SEO
// pages/index.vue
useSeoMeta({
title: 'MegaShop - Over 1 Million Products',
ogTitle: 'MegaShop - Premium E-Commerce Platform',
description: 'Shop over 1 million products with free worldwide shipping. Electronics, fashion, home & garden at the best prices.',
ogDescription: 'Discover 1 million+ products on MegaShop. Free shipping on orders over $50 USD.',
ogImage: '/images/og-homepage.jpg',
ogUrl: 'https://megashop.com',
ogType: 'website'
})
Output:
// Execution Successful
5. JSON-LD Structured Data
(1) The Impact of Structured Data on Search Results
| Data Type | Google Rich Snippet Results | Compatible with MegaShop |
|---|---|---|
| Product | Price/Rating/Inventory | ✅ Product Details Page |
| BreadcrumbList | Breadcrumb Navigation | ✅ All Pages |
| Organization | Company Information | ✅ Home |
| Website | Site Search Box | ✅ Home |
| Review | Review Summary | ✅ Product Page |
(1) ▶ Example: Product JSON-LD
<!-- pages/products/[id].vue -->
<script setup lang="ts">
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)
// JSON-LD structured data for Google rich results
useHead({
script: [
{
type: 'application/ld+json',
innerHTML: computed(() => JSON.stringify({
'@context': 'https://schema.org',
'@type': 'Product',
name: product.value?.name,
image: product.value?.image,
description: product.value?.description,
sku: `MEGA-${product.value?.id}`,
offers: {
'@type': 'Offer',
url: `https://megashop.com/products/${route.params.id}`,
priceCurrency: 'USD',
price: product.value?.price,
availability: product.value?.inStock
? 'https://schema.org/InStock'
: 'https://schema.org/OutOfStock'
},
aggregateRating: {
'@type': 'AggregateRating',
ratingValue: product.value?.rating,
reviewCount: product.value?.reviewCount
}
}))
}
]
})
</script>
Output:
// Execution Successful
(2) ▶ Example: Breadcrumb JSON-LD
// composables/useBreadcrumbLd.ts
export function useBreadcrumbLd(items: { name: string; url: string }[]) {
useHead({
script: [{
type: 'application/ld+json',
innerHTML: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: items.map((item, index) => ({
'@type': 'ListItem',
position: index + 1,
name: item.name,
item: item.url
}))
})
}]
})
}
// Usage in page
useBreadcrumbLd([
{ name: 'Home', url: 'https://megashop.com' },
{ name: 'Products', url: 'https://megashop.com/products' },
{ name: 'Premium Headphones', url: 'https://megashop.com/products/123' }
])
Output:
// Execution Successful
6. Sitemap and robots.txt
(1) ▶ Example: Installing @nuxtjs/sitemap
npm install @nuxtjs/sitemap
Output:
# Command executed successfully
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/sitemap'],
sitemap: {
hostname: 'https://megashop.com',
gzip: true,
routes: async () => {
// Dynamic routes for 1 million products
const products = await $fetch('/api/products/all-ids')
return products.map((id: number) => ({
loc: `/products/${id}`,
lastmod: new Date().toISOString(),
changefreq: 'daily',
priority: 0.8
}))
},
defaults: {
changefreq: 'weekly',
priority: 0.5
}
}
})
(2) ▶ Example: robots.txt
// public/robots.txt
User-agent: *
Allow: /
Disallow: /admin/
Disallow: /api/
Disallow: /profile/
Sitemap: https://megashop.com/sitemap.xml
Output:
Execution Successful
(1) Comparison of Sitemap Strategies
| Strategy | Advantages | Disadvantages | Applicability |
|---|---|---|---|
| Single-file sitemap.xml | Simple | ≤ 50,000 URLs | Small site |
| Sitemap Index | Supports millions of URLs | Requires splitting | MegaShop |
| Dynamically Generate Routes | Automatically Overwrite New Pages | Build-Time Queries | Product Pages |
| Automatic module generation | Zero configuration | Limited | Small to medium-sized sites |
7. Comprehensive Example: Complete SEO Guide for MegaShop Product Pages
<!-- pages/products/[id].vue -->
<template>
<div v-if="product" class="product-page">
<h1>{{ product.name }}</h1>
<p class="price">${{ product.price }} USD</p>
<p>{{ product.reviewCount }} reviews · {{ product.rating }} stars</p>
</div>
</template>
<script setup lang="ts">
const route = useRoute()
const config = useRuntimeConfig()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)
const productUrl = `${config.public.siteUrl}/products/${route.params.id}`
// SEO Meta
useSeoMeta({
title: () => `${product.value?.name} - MegaShop`,
ogTitle: () => product.value?.name,
description: () => `Buy ${product.value?.name} for $${product.value?.price} USD. Free shipping. ${product.value?.reviewCount} reviews.`,
ogDescription: () => `${product.value?.name} - $${product.value?.price} USD on MegaShop`,
ogImage: () => product.value?.image,
ogUrl: () => productUrl,
ogType: 'product',
twitterCard: 'summary_large_image'
})
// Canonical URL
useHead({
link: [{ rel: 'canonical', href: productUrl }]
})
// JSON-LD
useHead({
script: [{
type: 'application/ld+json',
innerHTML: computed(() => JSON.stringify({
'@context': 'https://schema.org',
'@type': 'Product',
name: product.value?.name,
image: product.value?.image,
offers: {
'@type': 'Offer',
priceCurrency: 'USD',
price: product.value?.price,
availability: product.value?.inStock ? 'InStock' : 'OutOfStock'
},
aggregateRating: {
'@type': 'AggregateRating',
ratingValue: product.value?.rating,
reviewCount: product.value?.reviewCount
}
}))
}]
})
</script>
❓ FAQ
useSeoMeta and useHead be used together?Head instance. useSeoMeta manages SEO tags, while useHead manages other tags (canonical/JSON-LD/style).innerHTML pose an XSS risk?innerHTML of script tags. However, it's best to use JSON.stringify and escape dynamic data to prevent injection.titleTemplate: '%s | MegaShop' in nuxt.config.ts. In the page's useHead, simply set the title content, and the template will automatically be constructed.📖 Summary
useHeadis a general-purpose manager forHeadtags, whileuseSeoMetafocuses on SEO metadata and offers greater type safety- Dynamic SEO: Computed, responsive titles and descriptions; unique metadata for each product page
- JSON-LD structured data enables Google to display rich snippets (price, rating, availability)
- @nuxtjs/sitemap automatically generates a sitemap and supports a sitemap index containing millions of URLs
- robots.txt controls the scope of crawler access and blocks the admin/api/profile paths
📝 Exercises
- Basic Exercise (Difficulty: ⭐): Use
useHeadto set the title and meta description for the homepage, then view the source code to verify. - Advanced Exercise (Difficulty: ⭐⭐): Use
useSeoMetato configure complete OG and Twitter Card metadata for the product detail page, and validate it using the Facebook Sharing Debugger. - Challenge (Difficulty: ⭐⭐⭐): Implement automatic generation of JSON-LD for product pages, JSON-LD for breadcrumbs, and sitemaps, and validate the structured data using the Google Rich Results Test.
---|



