DEV Community

Arindam Dawn
Arindam Dawn

Posted on

Stop Wrestling with JSON-LD: Type-Safe Structured Data for Next.js

Structured data is one of those things every developer knows they should implement, but few actually do. Why? Because writing JSON-LD by hand is error-prone, tedious, and easy to get wrong.
What if you could generate type-safe structured data with autocomplete, validation, and zero runtime overhead?

The Problem with JSON-LD

Here's what typical JSON-LD looks like:

{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "My Blog Post",
  "author": {
    "@type": "Person",
    "name": "Jane Doe"
  },
  "datePublished": "2026-02-10"
}
Enter fullscreen mode Exit fullscreen mode

Looks simple, right? But try maintaining this across dozens of pages:

  • Did you forget the @context?
  • Is datePublished in the right format?
  • Did you typo Article as Aticle?
  • Is this even valid according to schema.org? These mistakes cost you search rankings and AI visibility.

Enter Schema Sentry

Schema Sentry is a type-safe library for generating JSON-LD structured data. It gives you:
Full TypeScript autocomplete - No more guessing field names

Compile-time validation - Catch errors before they reach production

Zero runtime overhead - Everything happens at build time

Works with both Pages Router and App Router - Your choice of Next.js patterns

CI/CD validation - Automated checks in your pipeline

Quick Example

Instead of hand-writing JSON:

// ❌ Error-prone manual JSON
<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{
    __html: JSON.stringify({
      "@context": "https://schema.org",
      "@type": "Article",
      "headline": "My Post",
      // Did I get all required fields? Who knows!
    })
  }}
/>
Enter fullscreen mode Exit fullscreen mode

Use type-safe builders:

import { Schema, Article } from "@schemasentry/next";
// ✅ Type-safe with autocomplete
const article = Article({
  headline: "My Post",
  authorName: "Jane Doe",
  datePublished: "2026-02-10",
  url: "https://example.com/blog/post",
});
export default function Page() {
  return <Schema data={article} />;
}
Enter fullscreen mode Exit fullscreen mode

Same output, zero guesswork.

Pages Router vs App Router

Good news: Schema Sentry works identically with both Next.js routing patterns.

App Router (Next.js 13+)

// app/blog/[slug]/page.tsx
import { Schema, Article, BreadcrumbList } from "@schemasentry/next";
export default function BlogPost({ params }: { params: { slug: string } }) {
  const article = Article({
    headline: "Getting Started",
    authorName: "Jane Doe",
    datePublished: "2026-02-10",
    url: `https://example.com/blog/${params.slug}`,
  });
  const breadcrumbs = BreadcrumbList({
    items: [
      { name: "Home", url: "https://example.com" },
      { name: "Blog", url: "https://example.com/blog" },
    ],
  });
  return (
    <>
      <Schema data={[article, breadcrumbs]} />
      <article>{/* content */}</article>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Pages Router (Classic Next.js)

// pages/blog/[slug].tsx
import Head from "next/head";
import { Schema, Article, BreadcrumbList } from "@schemasentry/next";
export default function BlogPost() {
  const article = Article({
    headline: "Getting Started",
    authorName: "Jane Doe",
    datePublished: "2026-02-10",
    url: "https://example.com/blog/post",
  });
  const breadcrumbs = BreadcrumbList({
    items: [
      { name: "Home", url: "https://example.com" },
      { name: "Blog", url: "https://example.com/blog" },
    ],
  });
  return (
    <>
      <Head>
        <title>Getting Started - My Blog</title>
      </Head>
      <Schema data={[article, breadcrumbs]} />
      <article>{/* content */}</article>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

The only difference? App Router doesn't need next/head because it supports native metadata.

Supported Schema Types

Schema Sentry includes builders for the most common types:

  • Organization - Company/publisher info
  • WebSite - Site metadata
  • Article / BlogPosting - Blog posts and articles
  • Product - E-commerce products with offers
  • FAQPage - FAQ sections with expandable questions
  • HowTo - Step-by-step guides
  • BreadcrumbList - Navigation breadcrumbs
  • Person - Author/contributor info ## CI/CD Validation Here's where it gets powerful. Schema Sentry includes a CLI for automated validation:
// schema-sentry.manifest.json
{
  "routes": {
    "/": ["Organization", "WebSite"],
    "/blog": ["WebSite"],
    "/blog/getting-started": ["Article"],
    "/products/widget": ["Organization", "Product"],
    "/faq": ["FAQPage", "Organization"]
  }
}
Enter fullscreen mode Exit fullscreen mode

Add to your GitHub Actions:

# .github/workflows/schema-check.yml
name: Schema Validation
on: [push, pull_request]
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v2
      - run: pnpm install
      - run: pnpm build
      - run: pnpm schemasentry validate
Enter fullscreen mode Exit fullscreen mode

Now your CI will fail if:

  • A page is missing expected schema
  • Schema types don't match the manifest
  • Invalid JSON-LD is generated Never ship broken structured data again. ## Why Structured Data Matters ### For Traditional SEO Google uses JSON-LD to create rich snippets in search results:
  • Article headlines and publication dates
  • Product prices and ratings
  • FAQ dropdowns
  • Breadcrumb navigation These rich results get higher click-through rates than plain blue links. ### For AI Discovery This is the big one that most developers are missing. AI systems like:
  • ChatGPT
  • Claude
  • Perplexity
  • Google Bard ...all use structured data to understand and cite content. Without proper schema, your content is invisible to the AI discovery layer. ## Getting Started
# Install packages
npm install @schemasentry/next
npm install -D @schemasentry/cli
# Or with pnpm
pnpm add @schemasentry/next
pnpm add -D @schemasentry/cli
Enter fullscreen mode Exit fullscreen mode

Create your first schema:

import { Schema, Organization } from "@schemasentry/next";
const org = Organization({
  name: "My Company",
  url: "https://example.com",
  logo: "https://example.com/logo.png",
  description: "We build amazing things",
});
export default function Home() {
  return (
    <>
      <Schema data={org} />
      <h1>Welcome</h1>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

That's it! The <Schema /> component injects the JSON-LD into your page's <head> automatically.

Live Examples

Check out the complete working examples:

  • App Router Example - Modern Next.js 13+ patterns
  • Pages Router Example - Classic Next.js patterns Both demonstrate the same UI with all 11+ schema types. ## Summary Adding structured data doesn't have to be painful:
  • Type safety catches errors at compile time
  • Autocomplete guides you to the right fields
  • CI validation prevents broken schema from shipping
  • Zero runtime cost - everything is static
  • Works with any Next.js routing pattern Your future self (and your search rankings) will thank you. --- Have you implemented structured data on your Next.js site? What challenges did you face? Schema Sentry is open source under MIT license. Star us on GitHub if you found this helpful!

Top comments (0)