NextjsStructured Data (JSON-LD)

Structured Data (JSON-LD)

Structured data is machine-readable markup embedded in a page that describes its content in a standard vocabulary — "this page is an Article, published on this date, by this author" or "this page is a Product priced at $29.99, currently in stock." Search engines use it to build rich results: star ratings, prices, breadcrumbs, and FAQ dropdowns shown directly in search listings.

JSON-LD format
The most common format is JSON-LD — plain JSON following the schema.org vocabulary, embedded in a <script type="application/ld+json"> tag. It doesn't render visually; it's purely metadata for crawlers.
Adding it to a page
In a Server Component, render a <script> tag with its type set to application/ld+json and its content set via dangerouslySetInnerHTML, using JSON.stringify on your data object.

app/blog/[slug]/page.tsx — Article structured data

TSX
import { getPost } from '@/libs/posts'

export default async function BlogPostPage({
  params,
}: {
  params: { slug: string }
}) {
  const post = await getPost(params.slug)

  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'Article',
    headline: post.title,
    datePublished: post.publishedAt,
    dateModified: post.updatedAt,
    author: {
      '@type': 'Person',
      name: post.author.name,
    },
    image: post.coverImage,
  }

  return (
    <article>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      <h1>{post.title}</h1>
      <div>{post.content}</div>
    </article>
  )
}
Worked example: a Product page

app/shop/[slug]/page.tsx — Product structured data

TSX
import { getProduct } from '@/libs/products'

export default async function ProductPage({
  params,
}: {
  params: { slug: string }
}) {
  const product = await getProduct(params.slug)

  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'Product',
    name: product.name,
    image: product.image,
    description: product.description,
    offers: {
      '@type': 'Offer',
      price: product.price,
      priceCurrency: 'USD',
      availability: product.inStock
        ? 'https://schema.org/InStock'
        : 'https://schema.org/OutOfStock',
    },
  }

  return (
    <>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      <h1>{product.name}</h1>
      <p>{'$'}{product.price}</p>
    </>
  )
}
Note
Structured data improves click-through rate by making your search result more eye-catching — star ratings, prices, and images shown directly in the results page — it does not directly influence your ranking position. Search engines are also selective about which rich results they actually render even when the markup is valid, so treat it as "giving yourself a chance at a better-looking result," not a guaranteed outcome.
Because JSON-LD is just JSON, the same generateMetadata/data-fetching functions that build your title and description can also build the JSON-LD object — no separate fetch is required if the page already loads the underlying data for rendering.
  • Structured data is machine-readable markup (commonly JSON-LD) describing a page's content using the schema.org vocabulary.

  • Embed it via a <script type="application/ld+json"> tag with dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}.

  • Common types: Article for blog posts, Product for e-commerce, BreadcrumbList for navigation, FAQPage for FAQ sections.

  • It can improve click-through rate via rich search results (stars, prices, images) but does not directly affect ranking.

  • Reuse data you already fetched for the page body to build the JSON-LD object — no extra request needed.