Modul 9: Nuxt Fortgeschritten

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.

pages/produkt/[id].vue
<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:

pages/blog/[slug].vue
<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
<!-- 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:

pages/kontakt.vue
<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:

pages/blog/[slug].vue
<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.):

pages/produkt/[id].vue
<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:

Terminal
# Offizielle Nuxt SEO-Module installieren
npx nuxi module add @nuxtjs/sitemap
npx nuxi module add @nuxtjs/robots
nuxt.config.ts
// 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

AufgabeNuxtRails
Meta-TagsuseHead() / useSeoMeta()meta-tags Gem
SeitentiteltitleTemplatecontent_for :title
Sitemap@nuxtjs/sitemapsitemap_generator Gem
Structured DatauseHead() mit Script-TagManuelles JSON-LD im View