Strukturierte Daten in Next.js, aus einem Graphen erzeugt
Die meisten JSON-LD-Implementierungen liefern vier zusammenhanglose Blöcke, die jeweils die Organisation neu deklarieren. Ein Graph mit stabilen @id-Referenzen ist weniger Code - und auflösbar.
Strukturierte Daten sind der Teil des technischen SEO mit der größten Lücke
zwischen dem, was Teams ausliefern, und dem, was die Spezifikation belohnt.
Die übliche Umsetzung ist eine Komponente pro Seitentyp, jede mit eigenem
<script type="application/ld+json">, jede mit eigener Deklaration von
Organisation, Website und Logo.
Das validiert. Es sagt einem Crawler aber auch, dass Ihre Website vier unverbundene Organisationen enthält, die zufällig denselben Namen tragen.
Ein Graph statt vier Blöcke
Der Schlüssel @graph existiert genau dafür. Er enthält eine Liste von
Knoten, jeder mit einer stabilen @id, und die Knoten verweisen über diese
@id aufeinander, statt einander zu wiederholen.
{
"@context": "https://schema.org",
"@graph": [
{ "@type": "Organization", "@id": "https://example.com/#org", "name": "Example" },
{ "@type": "WebSite", "@id": "https://example.com/#site", "publisher": { "@id": "https://example.com/#org" } },
{ "@type": "WebPage", "@id": "https://example.com/blog/post#page", "isPartOf": { "@id": "https://example.com/#site" } },
{ "@type": "Article", "@id": "https://example.com/blog/post#article", "mainEntityOfPage": { "@id": "https://example.com/blog/post#page" } }
]
}Vier Knoten, eine Organisation, und jede Beziehung genau einmal ausgesprochen.
Die @id-Werte sind der eigentliche Mechanismus: Sie erlauben einem Crawler,
den Knoten hier mit demselben Knoten auf jeder anderen Seite zu verschmelzen.
Zwei Regeln machen sie brauchbar:
- Immer absolute URLs.
#orgallein ist kein Bezeichner, sondern ein Fragment, das auf jeder Seite etwas anderes bedeutet. - Für immer stabil. Eine
@id, die sich zwischen Deployments ändert - weil sie aus einem bearbeiteten Slug oder einer Datenbank-ID gebaut wurde -, verwirft alles, was der Crawler dazu gesammelt hatte.
Erzeugen statt schreiben
Handgeschriebenes JSON-LD driftet, weil nichts es zwingt, mit der Seite
übereinzustimmen. Der Titel ändert sich im metadata-Export und die
headline in den strukturierten Daten behält den alten, weil es zwei Strings
in zwei Dateien sind.
Die Lösung ist ein typisierter Helfer, aufgerufen dort, wo die Antwort schon bekannt ist:
// lib/jsonld.ts
export function articleNode({ url, title, description, published, modified }: ArticleInput) {
return {
'@type': 'Article',
'@id': `${url}#article`,
headline: title,
description,
datePublished: published,
dateModified: modified ?? published,
mainEntityOfPage: { '@id': `${url}#page` },
publisher: { '@id': `${SITE}#org` },
};
}// app/blog/[slug]/page.tsx
<JsonLd data={graph(
webPageNode({ url, title: doc.title, description: doc.description }),
articleNode({ url, title: doc.title, description: doc.description,
published: doc.date, modified: doc.updated }),
breadcrumbNode(trail),
)} />Jetzt können die strukturierten Daten der Seite nicht mehr widersprechen, weil sie dieselben Variablen lesen, die die Seite rendert. Das ist dasselbe Prinzip wie bei Canonicals und Sitemaps, die nicht auseinanderlaufen können: eine Quelle der Wahrheit, an beiden Enden erzeugt.
Wo es in die Antwort gehört
In das serverseitig gerenderte HTML. Ein <script>, das eine Client-Komponente
nach der Hydration einfügt, ist Markup, das ein Crawler ausführen kann oder
auch nicht - und es gibt keinen Grund, die Wette einzugehen, denn die Daten
sind auf dem Server bekannt.
export function JsonLd({ data }: { data: object }) {
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
/>
);
}dangerouslySetInnerHTML ist hier korrekt und keine Abkürzung: React escapt
Textkinder, was das JSON zerstört. Was Sie nicht tun dürfen, ist
nutzergesteuerten Text ohne Escaping von < einzusetzen - ein Kommentar, der
</script> enthält, schließt sonst das Tag und führt aus, was folgt.
Welche Typen sich lohnen und welche Theater sind
Strukturierte Daten verdienen ihren Platz, wenn sie auf ein Rich Result abbilden oder eine Entität auflösen. Darüber hinaus sind sie Markup, das niemand liest.
Lohnen sich:
OrganizationundWebSite, einmal, überall referenziert.BreadcrumbList, das direkt in den Ergebnissen erscheint.Articleauf Beiträgen,Productmit echtenOffer-Daten auf Produkten.FAQPage, aber nur dort, wo die Fragen sichtbar auf der Seite stehen. Nicht gerenderte Fragen auszuzeichnen ist ein Richtlinienverstoß, kein Trick.LocalBusinessoder ein Subtyp, wo es eine echte Adresse gibt. Die Subtypen verlangenaddress-ProfessionalServiceohne Adresse ist ungültig, und das ist ein verbreiteter Weg, einen kaputten Graphen auszuliefern und ihn für reichhaltiger zu halten.
Lohnen sich kaum:
Review, das Sie über sich selbst geschrieben haben. Eigenlob-Markup ist seit Jahren nicht mehr für Rich Results zugelassen.HowToundRecipeauf Seiten, die weder das eine noch das andere sind.speakable,SiteNavigationElementund der Rest des langen Schwanzes, den keine Oberfläche konsumiert.
Prüfen
Zwei Werkzeuge, die verschiedene Fragen beantworten. Der Rich Results Test
sagt Ihnen, ob eine Seite für einen bestimmten Ergebnistyp infrage kommt. Der
Schema Markup Validator sagt Ihnen, ob Ihr Graph wohlgeformt ist - er ist der,
der eine ins Leere zeigende @id-Referenz findet, die das erste Werkzeug
fröhlich ignoriert.
Führen Sie beide aus, auf dem gerenderten HTML der ausgelieferten Seite und nicht auf einem eingefügten Schnipsel. Der Schnipsel ist, was Sie geschrieben haben; das gerenderte HTML ist, was Sie ausgeliefert haben - und auf Seiten mit einer Caching-Schicht davor sind das nicht zuverlässig dieselben Dinge.
