La forma más fiable de insertar JSON-LD en App Router es generar directamente un bloque `<script type="application/ld+json">` desde un Server Component. De este modo, los datos estructurados aparecen en el HTML inicial que devuelve el servidor y el rastreador de IA puede leerlos sin ejecutar JavaScript. Para los motores de respuesta, el JSON-LD añadido después mediante useEffect o Google Tag Manager es prácticamente invisible: la mayoría termina de rastrear la página y se marcha sin esperar a que se complete la hidratación.
¿Por qué debe generarse en el servidor?
La diferencia está en el momento de ejecución. El Server Component escribe el JSON-LD en la cadena HTML cuando recibe la solicitud. Por tanto, el primer archivo que obtienen rastreadores como GPTBot, ClaudeBot, PerplexityBot y Google-Extended ya contiene el esquema completo. La inserción en el cliente, en cambio, exige esperar a que el navegador descargue y ejecute el bundle, un paso que la mayoría de los rastreadores de IA ni siquiera realiza. Aquí hay una trampa fácil de pasar por alto: Google Rich Results Test sí renderiza JavaScript. Los datos insertados en el cliente parecen correctos dentro de la herramienta y pueden dar la impresión de haber superado la prueba; sin embargo, cuando el motor de respuesta consulta la página, recibe el HTML original sin renderizar, donde el esquema no existe. En las auditorías técnicas que realizamos para nuestros clientes, esta es una de las carencias más frecuentes y menos evidentes.
Ejemplo mínimo viable: una página page.tsx
La implementación es muy breve. No requiere paquetes adicionales ni rutas de API: todo se resuelve dentro del Server Component que renderiza el artículo. Solo hay tres tareas esenciales: obtener los datos, agrupar los objetos y generar el script.
- En `app/blog/[slug]/page.tsx`, que de forma predeterminada es un Server Component, utiliza primero `await` para obtener los datos del artículo.
- Crea un objeto JavaScript puro llamado `jsonLd`. Empieza con `"@context": "https://schema.org"` y `"@type": "Article"`, y completa dinámicamente los demás campos —headline, datePublished, author e image— con los datos del artículo.
- Añade al principio del JSX devuelto: `<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd).replace(/</g, "\\u003c") }} />`.
- No importes bibliotecas de cliente ni añadas "use client" al principio del archivo. Si lo haces, pasará a ser un Client Component y se perderá todo el trabajo anterior.
Cómo conectar varias entidades con @graph
Una página suele necesitar más de un esquema: la marca se representa como Organization; el artículo, como Article; la ruta de navegación, como BreadcrumbList; y la sección de preguntas y respuestas, como FAQPage. En lugar de generar cuatro etiquetas script independientes, resulta más limpio reunirlas en el array `@graph` de un único objeto JSON-LD y relacionarlas mediante `@id`. Por ejemplo, el `publisher` de Article puede apuntar al `@id` de Organization, mientras que el último elemento de BreadcrumbList puede apuntar al `@id` de la página actual. Así, el motor de respuesta obtiene en un solo análisis el mapa completo de relaciones entre entidades y disminuye el riesgo de encontrar nodos contradictorios o duplicados.
- Organization y WebSite: inclúyelos una sola vez en `app/layout.tsx` para compartirlos en todo el sitio. No los declares de nuevo en cada página.
- Article o BlogPosting: inclúyelo en el `page.tsx` del artículo y completa sus campos dinámicamente con los datos de la página actual.
- BreadcrumbList: organízalo según la jerarquía de rutas. Resulta especialmente útil para que la IA comprenda la estructura del sitio.
- FAQPage: añádelo únicamente cuando la página muestre una sección real de preguntas y respuestas. El texto de `acceptedAnswer` debe coincidir con el contenido visible; no inventes preguntas solo para incluir el esquema.

Cómo mantener el JSON-LD a largo plazo
Si los objetos del esquema quedan dispersos entre distintas páginas, en tres meses nadie querrá modificarlos. Nuestro enfoque consiste en extraer un archivo `lib/schema.ts` y usar los tipos de `schema-dts` (`import type { Article, WithContext } from "schema-dts"`) para restringir el valor devuelto por cada función generadora. Si el nombre o el tipo de un campo es incorrecto, TypeScript lo señalará durante la compilación. La página solo tiene que llamar a `buildArticleSchema(post)`, recibir un objeto con tipos seguros y generarlo. Cada cambio se aplicará entonces a todo el sitio. Las URL deben ser siempre absolutas y estar centralizadas en una constante `SITE_URL`, lo que evita discrepancias en los `@id` entre el sitio oficial y el dominio de vista previa.
Errores habituales y comprobaciones antes de publicar
- Los valores de `url`, `image` o `datePublished` del JSON-LD no coinciden con el contenido visible. El motor de respuesta detecta la discrepancia y reduce su confianza en toda la página.
- Utilizar rutas relativas en `@id` o `url` puede hacer que apunten al lugar equivocado al cambiar al dominio de vista previa.
- Olvidar escapar `<` o añadir por error `"use client"` al principio de la página, lo que convierte el script en contenido generado en el cliente.
- Antes de publicar, comprueba la sintaxis con Google Rich Results Test. Después, utiliza `curl` para obtener directamente el HTML original de la página y confirmar que el script forma parte de la respuesta inicial, en vez de aparecer únicamente en las DevTools del navegador. Esta es la comprobación más importante, porque reproduce exactamente lo que ve el rastreador de IA.
En la mayoría de los sitios que hemos auditado, el problema del JSON-LD no está en cómo se ha escrito el esquema, sino en que solo existe en el cliente. Si el servidor no lo devuelve dentro del HTML, para la IA es como si no existiera.— Tenten GEO Technical Audit
De la implementación a la citación
Trasladar el JSON-LD a un Server Component es el requisito mínimo para que el contenido sea «legible por máquinas». El siguiente paso consiste en alinear los nombres de las entidades, los enlaces internos y la coherencia entre páginas para que la IA pueda citar el sitio de forma estable como fuente de confianza. Si no sabes con certeza si los datos estructurados de tu sitio forman parte del HTML que ve el rastreador, puedes solicitar un diagnóstico GEO de 30 minutos. Revisaremos el código fuente obtenido directamente para ayudarte a detectar las carencias, en lugar de limitarnos a examinar la página renderizada.



