Der App Router hat next/head durch die typisierte Metadata API ersetzt. Es gibt zwei Einsatzarten – den statischen Export für feste Seiten, generateMetadata für dynamische:
// app/layout.tsx — Standardwerte für die ganze Website mit Titel-Template
import type { Metadata } from "next";
export const metadata: Metadata = {
metadataBase: new URL("https://example.com"),
title: {
template: "%s | Acme",
default: "Acme — Webentwicklungsstudio",
},
description: "Standardbeschreibung, die gilt, wenn eine Seite keine eigene setzt.",
};
// app/blog/[slug]/page.tsx — dynamische Metadaten pro Beitrag
export async function generateMetadata({ params }): Promise<Metadata> {
const post = await getPost(params.slug);
if (!post) return { title: "Beitrag nicht gefunden" };
return {
title: post.title, // wird über das Template zu „Titel des Beitrags | Acme"
description: post.excerpt,
};
}
Praktische Regeln, die sich tatsächlich auf Positionen auswirken:
- Title: ~50–60 Zeichen, das Haupt-Keyword nahe am Anfang, für jede Seite einzigartig. Google schreibt schwache Titel um – geben Sie ihm keinen Anlass.
- Description: ~150–160 Zeichen. Sie beeinflusst das Ranking nicht direkt, ist aber Ihr Werbetext in den Ergebnissen und treibt die Klickrate.
metadataBaseist Pflicht – ohne sie gehen relative Adressen von OG-Bildern und Canonicals in Produktion kaputt.- Deduplizieren Sie die Abfrage zwischen
generateMetadataund der Seitenkomponente per Reactcache()– sonst fragen Sie das CMS bei jeder Anfrage zweimal ab.
import { cache } from "react";
export const getPost = cache(async (slug: string) => {
return fetchPostFromCMS(slug);
});
Checkliste:
-
metadataBaseim Haupt-Layout gesetzt - Titel-Template einmal definiert, Seiten setzen nur ihren eigenen Teil
- Jede Seite hat einen einzigartigen Title und eine einzigartige Description
- CMS-Seiten nutzen
generateMetadatamit einem incache()gekapselten Fetch - Ersatz-Metadaten für 404 / fehlende Inhalte
