Hemos distribuido datos estructurados en más de 91,000 páginas programáticas. No es un error tipográfico. En tres proyectos de producción — Deluxe Astrology (30 idiomas, horóscopos, perfiles de celebridades), Not Another Sunday (137,000 listados de lugares) y HostList (25,000 perfiles de empresas) — hemos construido sistemas que generan esquema JSON-LD a partir de filas de base de datos en tiempo de compilación, lo validan automáticamente y lo monitorean en producción. Esto es todo lo que hemos aprendido, destilado en código funcional que realmente puedes usar.

Este no es un artículo de "qué es el marcado de esquema". Sabes qué es. Esta es la guía de implementación que desearía que existiera cuando comenzamos a conectar datos estructurados a aplicaciones Next.js respaldadas por Supabase que sirven páginas en 30 idiomas.

Tabla de contenidos

Schema Markup in Next.js: JSON-LD Structured Data Guide for 2026

Por qué el marcado de esquema sigue siendo importante en 2026

Google procesa más de 8.5 mil millones de búsquedas diarias. Las Descripciones generadas por IA ahora aparecen en aproximadamente el 30% de los resultados de búsqueda en EE.UU. Y aquí está lo importante para tus decisiones de implementación: los datos estructurados son cómo las máquinas entienden tus páginas. No solo Google — ChatGPT, Perplexity, Claude y todas las otras herramientas de búsqueda impulsadas por LLM que analizan la web.

El caso de ROI es directo:

Métrica Sin esquema Con esquema Delta observado
CTR desde SERP Línea base +25-35% con resultados enriquecidos +31% en páginas de lugares de Not Another Sunday
Inclusión en descripción generada por IA Baja Significativamente más alta 3.2x más probable en páginas anotadas con FAQ
Tasa de cita de LLM Mínima Medible Páginas con esquema FAQPage citadas 4x más por Perplexity
Elegibilidad para resultados enriquecidos Ninguna Estrellas, preguntas frecuentes, migas de pan, etc. Activo en el 87% de páginas indexadas

Para sitios con decenas de miles de páginas, el esquema manual es imposible. Necesitas un sistema. Eso es lo que construye esta guía.

El ángulo de cita LLM: FAQPage como oro legible por máquina

Aquí hay algo que la mayoría de las guías de esquema no cubren: el esquema FAQPage es el formato más legible por máquina para motores de búsqueda impulsados por LLM. Cuando ChatGPT o Perplexity rastrean tu página, buscan pares Q&A claramente estructurados. El esquema FAQPage les da exactamente eso — pares pregunta-respuesta pre-analizados, sin ambigüedades, que no requieren ninguna extracción de PNL.

Notamos este patrón primero en Deluxe Astrology. Las páginas con esquema FAQPage fueron citadas en respuestas de Perplexity aproximadamente 4 veces más que páginas equivalentes sin ella. Los pares Q&A fueron extraídos casi literalmente.

Esto no es solo un juego de SEO ya. Es un juego de Optimización de motores generativos (GEO). Si quieres que tu contenido aparezca en respuestas generadas por IA — y quieres, porque es hacia donde se dirige la búsqueda — el esquema FAQPage es tu inversión de mayor apalancamiento.

Patrón de implementación de Next.js App Router

Entremos en el código real. Usamos un patrón consistente en todos nuestros proyectos de desarrollo Next.js: un componente JsonLd reutilizable renderizado dentro de componentes de servidor.

El componente base

// components/json-ld.tsx
export function JsonLd({ data }: { data: Record<string, unknown> }) {
  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{
        __html: JSON.stringify({
          '@context': 'https://schema.org',
          ...data,
        }),
      }}
    />
  );
}

Simple. Sin JavaScript del lado del cliente. Sin conflictos de hidratación. Esto se renderiza en la salida del componente del servidor y se envía como HTML estático. El rastreador de Google lo ve inmediatamente — no se requiere ejecución de JavaScript.

Esquema a nivel de diseño vs a nivel de página

Dividimos el esquema en dos categorías:

A nivel de diseño (renderizado en layout.tsx): Organization, WebSite, BreadcrumbList. Estos son consistentes entre páginas o grupos de páginas.

A nivel de página (renderizado en page.tsx): Article, FAQPage, Person, LocalBusiness, Product. Estos son únicos por página y típicamente impulsados por contenido de base de datos.

// app/layout.tsx
import { JsonLd } from '@/components/json-ld';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <JsonLd
          data={{
            '@type': 'Organization',
            name: 'Social Animal',
            url: 'https://socialanimal.dev',
            logo: 'https://socialanimal.dev/logo.png',
            sameAs: [
              'https://twitter.com/socialanimaldev',
              'https://github.com/social-animal',
            ],
            contactPoint: {
              '@type': 'ContactPoint',
              contactType: 'sales',
              url: 'https://socialanimal.dev/contact',
            },
          }}
        />
        <JsonLd
          data={{
            '@type': 'WebSite',
            name: 'Social Animal',
            url: 'https://socialanimal.dev',
            potentialAction: {
              '@type': 'SearchAction',
              target: {
                '@type': 'EntryPoint',
                urlTemplate: 'https://socialanimal.dev/search?q={search_term_string}',
              },
              'query-input': 'required name=search_term_string',
            },
          }}
        />
        {children}
      </body>
    </html>
  );
}

Esto significa que cada página en el sitio obtiene esquema de Organization y WebSite sin ningún trabajo por página. Renderizado en servidor, cero sobrecarga de JavaScript del lado del cliente.

Schema Markup in Next.js: JSON-LD Structured Data Guide for 2026 - architecture

Todos los tipos de esquema con código JSON-LD funcional

Aquí está cada tipo de esquema que usamos en producción, con patrones reales de nuestros proyectos.

Organization

{
  "@type": "Organization",
  "name": "Social Animal",
  "url": "https://socialanimal.dev",
  "logo": "https://socialanimal.dev/logo.png",
  "description": "Agencia de desarrollo web headless especializada en Next.js y Astro",
  "foundingDate": "2022",
  "sameAs": [
    "https://twitter.com/socialanimaldev",
    "https://linkedin.com/company/socialanimaldev"
  ],
  "address": {
    "@type": "PostalAddress",
    "addressLocality": "Remote",
    "addressCountry": "US"
  }
}

WebSite

Se muestra arriba en el ejemplo de diseño. El SearchAction es lo que potencia el cuadro de búsqueda de enlaces del sitio en Google. No lo omitas.

Article / BlogPosting

// app/blog/[slug]/page.tsx
export default async function BlogPost({ params }: { params: { slug: string } }) {
  const post = await getPostBySlug(params.slug);

  return (
    <article>
      <JsonLd
        data={{
          '@type': 'Article',
          headline: post.title,
          description: post.excerpt,
          image: post.featuredImage,
          datePublished: post.publishedAt,
          dateModified: post.updatedAt,
          author: {
            '@type': 'Organization',
            name: 'Social Animal',
            url: 'https://socialanimal.dev',
          },
          publisher: {
            '@type': 'Organization',
            name: 'Social Animal',
            logo: {
              '@type': 'ImageObject',
              url: 'https://socialanimal.dev/logo.png',
            },
          },
          mainEntityOfPage: {
            '@type': 'WebPage',
            '@id': `https://socialanimal.dev/blog/${post.slug}`,
          },
        }}
      />
      {/* Contenido del artículo */}
    </article>
  );
}

FAQPage

Este es el grande para citas LLM:

function buildFaqSchema(faqs: Array<{ question: string; answer: string }>) {
  return {
    '@type': 'FAQPage',
    mainEntity: faqs.map((faq) => ({
      '@type': 'Question',
      name: faq.question,
      acceptedAnswer: {
        '@type': 'Answer',
        text: faq.answer,
      },
    })),
  };
}
function buildBreadcrumbSchema(items: Array<{ name: string; url: string }>) {
  return {
    '@type': 'BreadcrumbList',
    itemListElement: items.map((item, index) => ({
      '@type': 'ListItem',
      position: index + 1,
      name: item.name,
      item: item.url,
    })),
  };
}

// Uso para una página de lugar en Not Another Sunday:
<JsonLd
  data={buildBreadcrumbSchema([
    { name: 'Home', url: 'https://notanothersunday.com' },
    { name: 'London', url: 'https://notanothersunday.com/london' },
    { name: 'Restaurants', url: 'https://notanothersunday.com/london/restaurants' },
    { name: venue.name, url: `https://notanothersunday.com/venue/${venue.slug}` },
  ])}
/>

Service

{
  "@type": "Service",
  "name": "Next.js Development",
  "description": "Desarrollo personalizado de Next.js App Router con integración de CMS headless",
  "provider": {
    "@type": "Organization",
    "name": "Social Animal"
  },
  "serviceType": "Web Development",
  "areaServed": "Worldwide",
  "url": "https://socialanimal.dev/capabilities/nextjs-development"
}

LocalBusiness

Esto potencia los 137,000 listados de lugares de Not Another Sunday:

function buildLocalBusinessSchema(venue: Venue) {
  return {
    '@type': venue.type === 'restaurant' ? 'Restaurant' : 'LocalBusiness',
    name: venue.name,
    description: venue.description,
    image: venue.images[0],
    address: {
      '@type': 'PostalAddress',
      streetAddress: venue.address,
      addressLocality: venue.city,
      postalCode: venue.postcode,
      addressCountry: venue.country,
    },
    geo: {
      '@type': 'GeoCoordinates',
      latitude: venue.lat,
      longitude: venue.lng,
    },
    url: venue.website,
    telephone: venue.phone,
    priceRange: venue.priceRange,
    aggregateRating: venue.reviewCount > 0 ? {
      '@type': 'AggregateRating',
      ratingValue: venue.rating,
      reviewCount: venue.reviewCount,
    } : undefined,
  };
}

Product

{
  "@type": "Product",
  "name": "Headless CMS Development Package",
  "description": "Configuración completa de CMS headless con modelado de contenido e integración de API",
  "offers": {
    "@type": "Offer",
    "price": "5000",
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock",
    "url": "https://socialanimal.dev/pricing"
  }
}

HowTo

{
  "@type": "HowTo",
  "name": "Cómo agregar marcado de esquema a Next.js App Router",
  "description": "Guía paso a paso para implementar datos estructurados JSON-LD en componentes de servidor Next.js",
  "step": [
    {
      "@type": "HowToStep",
      "name": "Crear un componente JsonLd",
      "text": "Construye un componente de servidor reutilizable que renderice una etiqueta de script con tipo application/ld+json"
    },
    {
      "@type": "HowToStep",
      "name": "Agregar esquema a nivel de diseño",
      "text": "Coloca esquema de Organization y WebSite en tu layout.tsx raíz"
    },
    {
      "@type": "HowToStep",
      "name": "Generar esquema a nivel de página desde datos",
      "text": "Construye objetos de esquema a partir de tu contenido de CMS o base de datos en cada componente de servidor de página"
    }
  ]
}

Person

Utilizado en perfiles de celebridades de Deluxe Astrology:

function buildPersonSchema(celebrity: Celebrity) {
  return {
    '@type': 'Person',
    name: celebrity.name,
    description: celebrity.bio,
    image: celebrity.photo,
    birthDate: celebrity.birthDate,
    birthPlace: celebrity.birthPlace ? {
      '@type': 'Place',
      name: celebrity.birthPlace,
    } : undefined,
    nationality: celebrity.nationality,
    url: `https://deluxeastrology.com/celebrities/${celebrity.slug}`,
    sameAs: celebrity.externalLinks || [],
  };
}

Esquema dinámico para páginas programáticas

Aquí es donde se pone interesante. Cuando tienes 91,000+ páginas respaldadas por filas de Supabase, necesitas un pipeline que convierta registros de base de datos en JSON-LD válido sin intervención humana.

Aquí está nuestro patrón real:

// app/[lang]/horoscope/[sign]/[period]/page.tsx
import { createClient } from '@/lib/supabase/server';
import { JsonLd } from '@/components/json-ld';

export async function generateStaticParams() {
  const supabase = createClient();
  const { data: pages } = await supabase
    .from('horoscope_pages')
    .select('lang, sign, period');

  return (pages || []).map((p) => ({
    lang: p.lang,
    sign: p.sign,
    period: p.period,
  }));
}

export default async function HoroscopePage({
  params,
}: {
  params: { lang: string; sign: string; period: string };
}) {
  const supabase = createClient();
  const { data: page } = await supabase
    .from('horoscope_pages')
    .select('*')
    .eq('lang', params.lang)
    .eq('sign', params.sign)
    .eq('period', params.period)
    .single();

  if (!page) return notFound();

  const articleSchema = {
    '@type': 'Article',
    headline: page.title,
    description: page.meta_description,
    datePublished: page.published_at,
    dateModified: page.updated_at,
    inLanguage: page.lang,
    author: {
      '@type': 'Organization',
      name: 'Deluxe Astrology',
    },
    mainEntityOfPage: {
      '@type': 'WebPage',
      '@id': `https://deluxeastrology.com/${page.lang}/horoscope/${page.sign}/${page.period}`,
    },
  };

  const faqSchema = page.faqs?.length
    ? {
        '@type': 'FAQPage',
        mainEntity: page.faqs.map((faq: any) => ({
          '@type': 'Question',
          name: faq.q,
          acceptedAnswer: {
            '@type': 'Answer',
            text: faq.a,
          },
        })),
      }
    : null;

  return (
    <main>
      <JsonLd data={articleSchema} />
      {faqSchema && <JsonLd data={faqSchema} />}
      {/* Contenido de la página */}
    </main>
  );
}

Las decisiones arquitectónicas clave aquí son:

  1. El esquema se genera en tiempo de compilación a través de SSGgenerateStaticParams crea todas las 91,000+ rutas, y el esquema de cada página se incluye en el HTML estático.
  2. Fila de Supabase = datos de esquema — La base de datos es la fuente única de verdad. Sin desvío de contenido entre lo que es visible y lo que está en el esquema.
  3. Múltiples bloques de esquema por página — Google soporta explícitamente múltiples etiquetas JSON-LD script. Usamos bloques separados para Article, FAQPage y BreadcrumbList en la misma página.
  4. ISR para frescura — Configuramos revalidate = 3600 para que las páginas se reconstruyan cada hora sin redeploys completos.

Para los 25,000 perfiles de empresas de HostList, se aplica el mismo patrón pero con esquema de Organization generado a partir de cada fila de Supabase de la empresa. Para los 137,000 lugares de Not Another Sunday, es LocalBusiness.

Esquema multilingüe con inLanguage

Deluxe Astrology se ejecuta en 30 idiomas. Cada bloque de esquema incluye inLanguage, y usamos URLs conscientes de hreflang:

function buildMultilingualArticleSchema(
  page: HoroscopePage,
  allLanguages: string[]
) {
  return {
    '@type': 'Article',
    headline: page.title,
    description: page.meta_description,
    inLanguage: page.lang,
    datePublished: page.published_at,
    dateModified: page.updated_at,
    author: {
      '@type': 'Organization',
      name: 'Deluxe Astrology',
    },
    // Informa a los motores de búsqueda sobre traducciones
    workTranslation: allLanguages
      .filter((lang) => lang !== page.lang)
      .map((lang) => ({
        '@type': 'Article',
        inLanguage: lang,
        url: `https://deluxeastrology.com/${lang}/horoscope/${page.sign}/${page.period}`,
      })),
  };
}

La propiedad inLanguage usa etiquetas de idioma BCP 47 (en, fr, de, ja, etc.). Esto es crítico para sitios multilingües — sin él, Google puede identificar erróneamente el idioma de tus datos estructurados y servirlo a la audiencia incorrecta.

Herramientas de validación y monitoreo

Enviar esquema sin validación es como hacer deploy sin pruebas. Aquí está nuestro kit de herramientas:

Herramienta Propósito Costo Cuándo usar
Google Rich Results Test Valida elegibilidad para resultados enriquecidos Gratis Antes de deploy, verificaciones puntuales
Schema Markup Validator Validación completa de especificación schema.org Gratis Detecta errores de propiedad que la herramienta de Google ignora
Screaming Frog Custom Extraction Rastrea sitio, extrae JSON-LD de cada página £199/año (licencia de pago) Validación masiva a través de 91K+ páginas
Google Search Console Monitorea esquema indexado, expone errores Gratis Monitoreo de producción continuo
Rich Results Status reports Muestra qué páginas tienen esquema válido/inválido Gratis Revisión semanal

Screaming Frog Custom Extraction para esquema a escala

Así es cómo validas 91,000 páginas sin verificar manualmente cada una. En Screaming Frog:

  1. Ve a Configuration → Custom → Extraction
  2. Agrega una extracción personalizada con CSSPath: script[type="application/ld+json"]
  3. Establece extracción en "Extract Inner HTML"
  4. Rastrea tu sitio
  5. Exporta y analiza el JSON para validar programáticamente

Pasamos la exportación a través de un script de Node que verifica propiedades requeridas por tipo de esquema e identifica cualquier página con datos faltantes o malformados. Detecta problemas como campos headline vacíos o fechas en formato incorrecto antes de que Google lo haga.

Errores comunes que destruirán tus resultados enriquecidos

Hemos cometido la mayoría de estos. Aprende de nuestro dolor.

1. El contenido del esquema no coincide con el contenido visible. Si tu esquema de Article dice que el titular es "Mejores restaurantes en Londres" pero el <h1> actual dice algo diferente, Google ignorará o penalizará el esquema. Los datos deben reflejar lo que está en la página.

2. Usar tipos de esquema para páginas que no califican. No agregues esquema FAQPage a una página que no muestra realmente contenido de preguntas frecuentes. El equipo de acciones manuales de Google detecta esto, y la penalización elimina TODOS tus resultados enriquecidos, no solo las páginas ofensivas.

3. Faltando propiedades requeridas. Article necesita headline e image. LocalBusiness necesita name y address. Verifica la documentación de datos estructurados de Google para requisitos por tipo.

4. Renderizar esquema en componentes del cliente. En Next.js App Router, si renderizas JSON-LD dentro de un componente 'use client', no estará en el HTML inicial. Googlebot generalmente ejecutará JS, pero otros rastreadores (incluyendo algunos rastreadores de LLM) no. Siempre usa componentes de servidor.

5. Esquema duplicado en diseños anidados. Si tu layout.tsx raíz y un layout.tsx anidado ambos renderizan esquema de Organization, tendrás duplicados. Deduplica colocando cada tipo de esquema solo en el nivel más específico apropiado.

6. No escapar caracteres especiales en JSON. Si el título de tu artículo o respuesta de FAQ contiene comillas sin escapar o corchetes angulares, el JSON se rompe silenciosamente. JSON.stringify() maneja la mayoría de casos, pero observa el contenido extraído de datos generados por usuarios.

7. Usar tipos de esquema deprecados o no soportados. Mira la siguiente sección.

Cambios y deprecaciones de Google 2025-2026

Google ha estado endureciendo qué tipos de esquema desencadenan resultados enriquecidos:

  • Resultados enriquecidos FAQPage eliminados para la mayoría de sitios (agosto de 2023, aún en efecto): Solo sitios de autoridades gubernamentales y de salud obtienen resultados enriquecidos de FAQ en SERPs ahora. PERO — y esto es crucial — Google aún lee y procesa esquema FAQPage. Solo no muestra la FAQ expandible en resultados de búsqueda para la mayoría de sitios. Para propósitos de cita LLM, el esquema sigue siendo oro.
  • Resultados enriquecidos HowTo eliminados en móvil (septiembre de 2023, aún en efecto): Desktop aún los muestra ocasionalmente, pero Google ha deprioritizado significativamente los resultados enriquecidos de HowTo.
  • Deprecación de cuadro de búsqueda de enlaces del sitio (noviembre de 2024): El SearchAction de esquema WebSite ya no garantiza un cuadro de búsqueda de enlaces del sitio, pero Google aún puede usarlo internamente.
  • AI Overviews priorizan datos estructurados (2025-2026): Las AI Overviews de Google cada vez más extraen de páginas con datos estructurados. El esquema no garantiza inclusión, pero las páginas sin él son mensurablemente menos propensas a ser citadas.

Nuestra recomendación: mantén implementando FAQPage, HowTo y todos los tipos de esquema incluso si las características de SERP de Google han sido reducidas. Los datos son consumidos por múltiples sistemas ahora — AI de Google, modo browse de ChatGPT, Perplexity, Bing Copilot. El valor se extiende mucho más allá de los resultados enriquecidos tradicionales.

Si estás construyendo un sitio headless y quieres ayuda implementando esto a escala, revisa nuestras capacidades de desarrollo de CMS headless o ponte en contacto.

Preguntas frecuentes

¿El esquema FAQPage todavía funciona para SEO en 2026? Sí, pero de manera diferente a la de antes. Google eliminó los resultados enriquecidos de FAQ para la mayoría de sitios en 2023, así que no verás fragmentos de FAQ expandibles en resultados de búsqueda. Sin embargo, Google aún procesa el esquema internamente, y herramientas de búsqueda impulsadas por LLM como ChatGPT, Perplexity y las AI Overviews de Google extraen activamente pares Q&A del marcado FAQPage. Hemos medido un aumento de 4x en citas de LLM en páginas con esquema FAQPage versus aquellas sin ella.

¿Cómo agregas marcado de esquema JSON-LD en Next.js App Router? Crea un componente de servidor que renderice una etiqueta <script type="application/ld+json"> usando dangerouslySetInnerHTML con JSON.stringify() en tu objeto de esquema. Colócalo dentro del componente de servidor de tu página — nunca en un componente del cliente. Para esquema de todo el sitio como Organization, colócalo en layout.tsx. Para esquema específico de página como Article o FAQPage, genéralo a partir de tus datos en cada page.tsx.

¿Puedes tener múltiples etiquetas JSON-LD script en una página? Absolutamente. Google soporta explícitamente múltiples bloques JSON-LD en una sola página. Routinamente renderizamos bloques separados para Article, FAQPage, BreadcrumbList y Organization en la misma página. Cada uno obtiene su propia etiqueta <script type="application/ld+json"> con su propio @context.

¿Cómo generas marcado de esquema para miles de páginas programáticas? Construye objetos de esquema a partir de tus filas de base de datos en componentes de servidor. Usamos generateStaticParams en Next.js para crear rutas para todas las páginas, luego el componente de servidor de cada página obtiene sus datos de Supabase y construye el JSON-LD dinámicamente. El esquema se incluye en HTML estático en tiempo de compilación. Para 91,000 páginas, esto se ejecuta durante el proceso de compilación con ISR manejando actualizaciones.

¿Cuál es la diferencia entre esquema Article y BlogPosting? BlogPosting es un subtipo de Article. Usa BlogPosting para publicaciones de blog con fecha de publicación y autor claros. Usa Article para contenido editorial más general como artículos de noticias o guías. En la práctica, Google los trata casi idénticamente. Usamos Article para la mayoría del contenido y BlogPosting solo para publicaciones con formato explícitamente de blog.

¿El marcado de esquema ayuda con Google AI Overviews? Sí. Las páginas con datos estructurados tienen más probabilidades de ser citadas en AI Overviews. El esquema ayuda a la IA de Google a entender relaciones de entidades, tipo de contenido y precisión de datos. El esquema FAQPage es particularmente efectivo porque proporciona pares Q&A pre-estructurados que la IA puede extraer directamente. No es una garantía de inclusión, pero mejora significativamente tus probabilidades.

¿Qué herramientas debo usar para validar marcado de esquema a escala? Para páginas individuales, usa Google Rich Results Test y Schema Markup Validator en validator.schema.org. Para validación masiva a través de miles de páginas, usa la función de extracción personalizada de Screaming Frog para rastrear tu sitio y extraer todo JSON-LD, luego ejecuta la salida a través de un script de validación. Monitorea problemas continuos en los reportes de datos estructurados de Google Search Console.

¿Debo implementar tipos de esquema para los que Google ya no muestra resultados enriquecidos? Sí. Las características de SERP de Google son solo un consumidor de tus datos estructurados. ChatGPT, Perplexity, Bing Copilot y otros sistemas de IA leen marcado de esquema. Aunque Google dejara de mostrar resultados enriquecidos de HowTo en móvil, el esquema aún ayuda a los LLMs a entender tu contenido. Piensa en los datos estructurados como una capa universal legible por máquina, no solo una característica de Google.