Structured data pentru Next.js App Router: ghidul complet
Cum adaugi corect JSON-LD schema.org în Next.js App Router: pattern-ul @graph centralizat, convenții de @id, un nod per tip de pagină și cum eviți duplicatele care strică rich results.

Pe scurt: în Next.js App Router adaugi structured data ca JSON-LD, injectat printr-un component de <script type="application/ld+json">, ideal ca un singur @graph per pagină cu @id-uri stabile. Cel mai frecvent bug nu e schema lipsă, ci schema duplicată — același tip emis de două ori de pe surse diferite. Mai jos, pattern-ul care scalează.
De ce contează în App Router
App Router randează majoritatea paginilor pe server (Server Components), ceea ce e ideal pentru structured data: JSON-LD ajunge în HTML-ul inițial, exact ce vede crawlerul, fără să aștepte JavaScript. Nu ai nevoie de biblioteci externe — un simplu component care serializează un obiect e suficient.
Componentul JsonLd
Cel mai curat mod e un component minimal reutilizabil:
export default function JsonLd({ data }: { data: object }) {
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
/>
);
}
dangerouslySetInnerHTML sună periculos, dar aici e corect: serializezi un obiect pe care îl controlezi tu, nu input de utilizator. Îl folosești în orice Server Component, inclusiv în layout.tsx pentru schema globală și în page.tsx pentru schema specifică paginii.
Pattern-ul @graph centralizat
Greșeala clasică e să împrăștii noduri JSON-LD prin mai multe componente — unul din layout, altul din pagină, altul dintr-un component de breadcrumb, altul dintr-un FAQ. Rezultatul: aceeași pagină emite trei <script>-uri separate, cu noduri care se suprapun.
Soluția: un singur @graph per pagină, cu toate nodurile într-un array, fiecare cu un @id unic:
<JsonLd data={{
'@context': 'https://schema.org',
'@graph': [
{ '@type': 'WebPage', '@id': `${url}#webpage`, /* ... */ },
{ '@type': 'BreadcrumbList', '@id': `${url}#breadcrumb`, /* ... */ },
{ '@type': 'FAQPage', '@id': `${url}#faq`, /* ... */ },
],
}} />
Nodurile se referă între ele prin @id (ex: WebPage.breadcrumb → { '@id': '...#breadcrumb' }), formând un graf conectat pe care Google îl citește ca un întreg coerent.
Convenția de @id
Folosește @id-uri stabile bazate pe URL + fragment: ${url}#webpage, ${url}#breadcrumb, ${baseUrl}/#org pentru entitatea globală. Astfel:
- fiecare nod are o identitate unică și referabilă,
- entitatea de organizație (
/#org) e definită o singură dată global și referită din toate paginile, - eviți coliziunile între noduri de același tip pe URL-uri diferite.
Ce schema pe ce tip de pagină
- Home:
WebSite+Organization(global) +WebPage. - Index-uri (blog, proiecte):
CollectionPagecuhasPart. - Articol de blog:
BlogPosting+WebPage(envelope) +BreadcrumbList, opționalHowTo. - Pagină de serviciu:
Service+WebPage+BreadcrumbList+FAQPage. - Despre:
AboutPage; Contact:ContactPage— ambele subtipuri deWebPage. - Autor:
Person(author) +Organization(publisher) — pattern standard.
Capcana duplicatelor
Ăsta e bug-ul care apare cel mai des și e invizibil în cod — îl vezi doar în Rich Results Test. Sursele comune:
- un
siteGraphglobal în layout care emite unWebPagepe fiecare pagină, - pagina care emite propriul
WebPageexplicit → douăWebPage, - multi-type ca
['WebPage', 'CollectionPage']PLUS un al doilea nodCollectionPageseparat, - componente (Breadcrumb, FaqList) care au auto-emit propriu care coexistă cu graful centralizat.
Am scris un studiu de caz întreg despre asta — cum elimini duplicatele JSON-LD — cu cele patru surse și fix-ul pe fiecare.
Cum validezi
npx tsc --noEmit verifică tipurile, dar NU graful randat. Structured data se validează la runtime:
- Rich Results Test (search.google.com/test/rich-results) pe URL-ul live — confirmă că fiecare tip apare o singură dată.
- Schema Markup Validator (validator.schema.org) pentru validare pură schema.org.
- Search Console → Enhancements pentru erori la scară, după re-crawl (5-14 zile).
Regula: după fiecare pagină nouă cu schema, ruleaz-o prin Rich Results Test. Tipurile corecte în cod nu garantează @graph corect randat.
Rezumat
Structured data în App Router e simplu dacă respecți trei reguli: un singur @graph per pagină, @id-uri stabile bazate pe URL, și validare la runtime nu doar la compilare. Restul e maparea tipului de schema pe tipul de pagină.
Dacă vrei un audit al structured data existente cu fix-urile aplicate direct în cod, asta facem în auditul SEO/AI. Pentru monitoring și îmbunătățiri continue, e parte din SEO continuu.