Optimize Your Nuxt 4 Application SEO
Complete guide to optimize SEO in Nuxt 4. useHead, useSeoMeta, Open Graph, sitemap, structured data, and Core Web Vitals.
Written by Francisco Zapata
SEO (Search Engine Optimization) is crucial for the visibility of any web application. Nuxt 4, with its Server-Side Rendering and Static Site Generation capabilities, has a natural advantage over traditional SPAs. However, leveraging its full potential requires configuration and strategy. This guide covers everything you need to maximize your application's SEO.
useHead: Meta Tag Control
useHead is the fundamental composable for managing your page's <head>:
<script setup>
useHead({
title: 'My Page - Francisco Zapata',
meta: [
{ name: 'description', content: 'Page description' },
{ name: 'keywords', content: 'vue, nuxt, web development' }
],
link: [
{ rel: 'canonical', href: 'https://mydomain.com/page' }
],
htmlAttrs: {
lang: 'en'
}
})
</script>
Title Template
Define a global template for titles:
// app/app.vue
useHead({
titleTemplate: (titleChunk) => {
return titleChunk ? `${titleChunk} | Francisco Zapata` : 'Francisco Zapata - Web Developer'
}
})
In each page you only define the specific title:
<script setup>
useHead({ title: 'About Me' })
// Result: "About Me | Francisco Zapata"
</script>
useSeoMeta: Simplified SEO Meta Tags
useSeoMeta offers a type-safe and more readable API for SEO meta tags:
<script setup>
useSeoMeta({
title: 'Vue 3 Composables: Complete Guide',
description: 'Learn to create Vue 3 composables to reuse logic professionally.',
ogTitle: 'Vue 3 Composables: Complete Guide',
ogDescription: 'Learn to create Vue 3 composables to reuse logic professionally.',
ogImage: 'https://mydomain.com/images/composables-og.jpg',
ogUrl: 'https://mydomain.com/blog/composables-vue-3',
ogType: 'article',
ogSiteName: 'Francisco Zapata Blog',
twitterCard: 'summary_large_image',
twitterTitle: 'Vue 3 Composables: Complete Guide',
twitterDescription: 'Learn to create Vue 3 composables.',
twitterImage: 'https://mydomain.com/images/composables-og.jpg',
robots: 'index, follow'
})
</script>
Dynamic Meta Tags
For dynamic pages like blog posts:
<script setup>
const route = useRoute()
const { data: post } = await useFetch(`/api/posts/${route.params.slug}`)
useSeoMeta({
title: post.value?.title,
description: post.value?.excerpt,
ogTitle: post.value?.title,
ogDescription: post.value?.excerpt,
ogImage: post.value?.featuredImage,
ogType: 'article',
articlePublishedTime: post.value?.publishedAt,
articleAuthor: 'Francisco Zapata'
})
useHead({
script: [
{
type: 'application/ld+json',
innerHTML: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'BlogPosting',
headline: post.value?.title,
description: post.value?.excerpt,
image: post.value?.featuredImage,
datePublished: post.value?.publishedAt,
author: {
'@type': 'Person',
name: 'Francisco Zapata'
}
})
}
]
})
</script>
Open Graph and Twitter Cards
Open Graph
Open Graph controls how your page appears when shared on social media:
<script setup>
useSeoMeta({
ogTitle: 'Title for social media',
ogDescription: 'Description for social media',
ogImage: 'https://mydomain.com/og-image.jpg',
ogUrl: 'https://mydomain.com/page',
ogType: 'website',
ogSiteName: 'Francisco Zapata',
ogLocale: 'en_US',
ogLocaleAlternate: ['es_ES'],
ogImageWidth: 1200,
ogImageHeight: 630,
ogImageAlt: 'Image description'
})
</script>
Twitter Cards
<script setup>
useSeoMeta({
twitterCard: 'summary_large_image',
twitterSite: '@franciscozapata',
twitterCreator: '@franciscozapata',
twitterTitle: 'Article title',
twitterDescription: 'Brief description',
twitterImage: 'https://mydomain.com/twitter-image.jpg'
})
</script>
Sitemap
Generate an XML sitemap automatically with @nuxtjs/sitemap:
npm install @nuxtjs/sitemap
export default defineNuxtConfig({
modules: ['@nuxtjs/sitemap'],
site: {
url: 'https://mydomain.com'
},
sitemap: {
sources: [
'/api/__sitemap__/urls'
]
}
})
For dynamic routes, create an endpoint:
// server/api/__sitemap__/urls.ts
export default defineSitemapEventHandler(async () => {
const posts = await $fetch('/api/posts')
return posts.map((post) => ({
loc: `/blog/${post.slug}`,
lastmod: post.updatedAt,
changefreq: 'weekly',
priority: 0.8,
images: post.featuredImage ? [{ loc: post.featuredImage }] : []
}))
})
Structured Data (Schema.org)
Structured data helps search engines understand your content:
Person/Organization
useHead({
script: [
{
type: 'application/ld+json',
innerHTML: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'Person',
name: 'Francisco Zapata',
url: 'https://mydomain.com',
jobTitle: 'Full Stack Developer',
sameAs: [
'https://github.com/franciscozapata',
'https://linkedin.com/in/franciscozapata'
]
})
}
]
})
Core Web Vitals
Google uses Core Web Vitals as a ranking factor. Optimize them in Nuxt 4:
Largest Contentful Paint (LCP)
<template>
<NuxtImg
src="/hero.jpg"
alt="Hero"
loading="eager"
fetchpriority="high"
sizes="100vw"
format="webp"
/>
</template>
Cumulative Layout Shift (CLS)
img {
aspect-ratio: 16 / 9;
width: 100%;
height: auto;
}
Interaction to Next Paint (INP)
<script setup>
const HeavyChart = defineAsyncComponent(() =>
import('~/components/HeavyChart.vue')
)
</script>
@nuxtjs/seo Module
For an all-in-one solution, install @nuxtjs/seo:
npm install @nuxtjs/seo
export default defineNuxtConfig({
modules: ['@nuxtjs/seo'],
site: {
url: 'https://mydomain.com',
name: 'Francisco Zapata - Web Developer',
description: 'Web development blog and portfolio',
defaultLocale: 'en'
}
})
Conclusion
Nuxt 4 provides excellent SEO tools out of the box. With SSR/SSG, dynamic meta tags, structured data, and Core Web Vitals optimization, your application will be well-positioned in search engines. The key is consistency: define meta tags on every page, generate automatic sitemaps, and regularly monitor performance with tools like Lighthouse and Google Search Console.
Comments (1)
Leave a comment
Precioso! Qué manera de explicar. Gracias.