SEO & Meta-Tags
Optimiere deine Nuxt-App für Suchmaschinen mit useHead, useSeoMeta, Open Graph Tags und strukturierten Daten.
SEO in Nuxt
Dank Server-Side Rendering hat Nuxt von Haus aus gute SEO-Voraussetzungen – Suchmaschinen sehen fertiges HTML. Aber für optimale Rankings musst du Meta-Tags, Open Graph Daten und strukturierte Daten gezielt einsetzen.
Nuxt bietet dafür zwei Haupt-Composables:
- useHead() – Volle Kontrolle über den
<head>-Bereich - useSeoMeta() – Type-safe Helper speziell für SEO-Tags
Rails-Vergleich
In Rails nutzen viele das meta-tags Gem mit set_meta_tags in Controllern und display_meta_tags im Layout. In Nuxt übernehmen useHead() und useSeoMeta() diese Rolle – direkt in der Komponente, ohne separates Gem.
useHead() – Der Allrounder
Mit useHead() kontrollierst du den kompletten <head>-Bereich deiner Seite: Titel, Meta-Tags, Stylesheets, Scripts und mehr.
<script setup lang="ts">
const { data: produkt } = await useFetch(`/api/produkte/${route.params.id}`)
useHead({
title: produkt.value?.name,
meta: [
{ name: 'description', content: produkt.value?.beschreibung },
{ name: 'robots', content: 'index, follow' }
],
link: [
{ rel: 'canonical', href: `https://meinshop.de/produkt/${route.params.id}` }
],
htmlAttrs: {
lang: 'de'
}
})
</script>Reaktive Werte
useHead() akzeptiert auch ref()- und computed()-Werte. Die Meta-Tags werden automatisch aktualisiert, wenn sich die reaktiven Daten ändern.
useSeoMeta() – Type-Safe SEO
useSeoMeta() ist ein spezialisierter Helper mit Autocomplete für alle gängigen SEO-Meta-Tags. Statt verschachtelte Objekte zu schreiben, nutzt du flache, typisierte Properties:
<script setup lang="ts">
const { data: post } = await useFetch(`/api/blog/${route.params.slug}`)
useSeoMeta({
// Basis-Tags
title: post.value?.titel,
description: post.value?.zusammenfassung,
// Open Graph (Facebook, LinkedIn)
ogTitle: post.value?.titel,
ogDescription: post.value?.zusammenfassung,
ogImage: post.value?.bild,
ogType: 'article',
// Twitter Card
twitterCard: 'summary_large_image',
twitterTitle: post.value?.titel,
twitterDescription: post.value?.zusammenfassung,
twitterImage: post.value?.bild,
// Artikel-spezifisch
articlePublishedTime: post.value?.datum,
articleAuthor: post.value?.autor
})
</script>Warum useSeoMeta?
useSeoMeta() hat zwei Vorteile: TypeScript-Autocomplete für über 100 Meta-Tags und eine flachere, lesbarere API. Intern generiert es die gleichen Tags wie useHead().
Title Templates
Statt auf jeder Seite den kompletten Titel zu setzen, definierst du ein Template im Layout oder in app.vue:
<!-- app.vue -->
<script setup lang="ts">
useHead({
titleTemplate: (title) => {
return title ? `${title} – Mein Shop` : 'Mein Shop'
}
})
</script>
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>Jede Seite setzt dann nur noch ihren eigenen Titel:
<script setup lang="ts">
// Ergebnis: "Kontakt – Mein Shop"
useHead({ title: 'Kontakt' })
</script> Ergebnis: Kontakt – Mein Shop. Die titleTemplate-Funktion erhält den Seitentitel als Parameter. Wenn keine Seite einen Titel setzt, wird der Standardwert Mein Shop angezeigt.
Open Graph Tags für Social Media
Open Graph Tags bestimmen, wie deine Seite aussieht, wenn sie auf Twitter, Facebook, LinkedIn oder in Messaging-Apps geteilt wird:
<script setup lang="ts">
const config = useRuntimeConfig()
const route = useRoute()
const url = `${config.public.siteUrl}${route.path}`
useSeoMeta({
title: post.value?.titel,
description: post.value?.zusammenfassung,
// Open Graph
ogTitle: post.value?.titel,
ogDescription: post.value?.zusammenfassung,
ogImage: `${config.public.siteUrl}${post.value?.bild}`,
ogUrl: url,
ogType: 'article',
ogLocale: 'de_DE',
ogSiteName: 'Mein Blog',
// Twitter
twitterCard: 'summary_large_image',
twitterSite: '@meinblog',
twitterCreator: '@autor',
})
</script>Bild-URLs
Open Graph Bilder müssen absolute URLs sein (mit https://). Relative Pfade funktionieren nicht. Nutze useRequestURL() oder eine Umgebungsvariable für die Basis-URL.
Strukturierte Daten (JSON-LD)
Strukturierte Daten helfen Suchmaschinen, den Inhalt deiner Seite besser zu verstehen. Sie ermöglichen Rich Snippets in den Suchergebnissen (Sternebewertungen, FAQ-Akkordeons, etc.):
<script setup lang="ts">
const { data: produkt } = await useFetch(`/api/produkte/${route.params.id}`)
// Strukturierte Daten als JSON-LD einfügen
useHead({
script: [
{
type: 'application/ld+json',
innerHTML: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'Product',
name: produkt.value?.name,
description: produkt.value?.beschreibung,
image: produkt.value?.bild,
offers: {
'@type': 'Offer',
price: produkt.value?.preis,
priceCurrency: 'EUR',
availability: 'https://schema.org/InStock'
},
aggregateRating: {
'@type': 'AggregateRating',
ratingValue: produkt.value?.bewertung,
reviewCount: produkt.value?.anzahlBewertungen
}
})
}
]
})
</script>Test-Tool
Teste deine strukturierten Daten mit dem Google Rich Results Test (search.google.com/test/rich-results) oder dem Schema.org Validator.
Robots.txt & Sitemap
Für eine vollständige SEO-Strategie brauchst du auch eine robots.txt und eine Sitemap. Nuxt hat dafür offizielle Module:
# Offizielle Nuxt SEO-Module installieren
npx nuxi module add @nuxtjs/sitemap
npx nuxi module add @nuxtjs/robots// nuxt.config.ts
export default defineNuxtConfig({
modules: [
'@nuxtjs/sitemap',
'@nuxtjs/robots'
],
site: {
url: 'https://meinshop.de'
},
sitemap: {
// Dynamische URLs hinzufügen
sources: ['/api/__sitemap__/urls']
},
robots: {
// robots.txt Konfiguration
groups: [
{
userAgent: '*',
allow: '/',
disallow: ['/admin/', '/api/']
}
],
sitemap: 'https://meinshop.de/sitemap.xml'
}
}) Das @nuxtjs/sitemap-Modul generiert automatisch eine Sitemap aus deinen Routen. Dynamische Routen (z.B. Blog-Posts) werden automatisch erkannt, wenn du useFetch auf den Seiten verwendest.
Rails-Vergleich
In Rails nutzt du Gems wie sitemap_generator und eine statische public/robots.txt. In Nuxt übernehmen die offiziellen SEO-Module diese Aufgabe automatisch und generieren dynamische Sitemaps basierend auf deinen Routen.
Zusammenfassung
| Aufgabe | Nuxt | Rails |
|---|---|---|
| Meta-Tags | useHead() / useSeoMeta() | meta-tags Gem |
| Seitentitel | titleTemplate | content_for :title |
| Sitemap | @nuxtjs/sitemap | sitemap_generator Gem |
| Structured Data | useHead() mit Script-Tag | Manuelles JSON-LD im View |