All fixes
warning
Rule: structured-data-presentWeight: 4/100

Fix: Missing structured data (JSON-LD)

Missing structured data means search engines read your page as a flat text document instead of a structured database entry. Without JSON-LD, you forfeit rich results—FAQ dropdowns, recipe carousels, breadcrumbs, and review stars. These UI enhancements in the SERP are not cosmetic. An FAQ snippet routinely doubles the vertical pixel height of your search result, pushing competitors below the fold.

Indxel flags pages missing this machine-readable context with the structured-data-present rule. Google parses application/ld+json scripts to map your content to its Knowledge Graph. If you ship a blog post without Article schema, or a landing page without SoftwareApplication schema, you force Google's parser to guess your content's primary entity. Guessing leads to standard blue links instead of rich snippets.

The structured-data-present rule carries a severity weight of 4/100. It is classified as a warning because a page can still rank without structured data, but it will systematically underperform in click-through rate (CTR) compared to a page displaying rich results.

How do you detect missing structured data?

Run the Indxel CLI against your local build or production URL. The scanner evaluates the rendered DOM specifically for <script type="application/ld+json"> tags containing valid JSON objects with an @context of https://schema.org.

npx indxel check http://localhost:3000
 
# Output
Running Indxel SEO audit...
 
/blog/how-to-configure-redis
  ⚠ structured-data-present  No JSON-LD structured data found on page. (Weight: 4)
 
/pricing
  ⚠ structured-data-present  No JSON-LD structured data found on page. (Weight: 4)
 
✖ 2 warnings found. Score: 96/100.

The structured-data-present rule only verifies the existence of a valid JSON-LD script tag. It does not validate the specific schema requirements (e.g., missing a required author field in an Article). Schema-specific validation is handled by the invalid-structured-data rule.

How to fix missing structured data in Next.js App Router

The Next.js App Router handles metadata natively, but it does not auto-generate JSON-LD. You must inject it manually into your page components. Because JSON-LD is just a script tag containing a JSON string, you inject it using React's dangerouslySetInnerHTML.

The optimal pattern is to create a dedicated TypeScript component for your schema to ensure type safety before stringification.

Bad: Standard page without schema

This page renders standard HTML. Search engines extract the h1 and p tags, but they do not definitively know this is a tutorial with discrete steps.

// app/docs/setup/page.tsx
export default function SetupGuide() {
  return (
    <article>
      <h1>How to set up the Indxel CLI</h1>
      <p>Follow these steps to configure your environment.</p>
      <h2>Step 1: Install</h2>
      <p>Run npm i -g indxel</p>
    </article>
  );
}

Good: Page with injected HowTo JSON-LD

Inject the <script> tag directly into the JSX. Since React escapes string content by default, you must pass the stringified JSON object to dangerouslySetInnerHTML.

// app/docs/setup/page.tsx
import { WithContext, HowTo } from 'schema-dts';
 
export default function SetupGuide() {
  const jsonLd: WithContext<HowTo> = {
    '@context': 'https://schema.org',
    '@type': 'HowTo',
    name: 'How to set up the Indxel CLI',
    description: 'Follow these steps to configure your environment.',
    step: [
      {
        '@type': 'HowToStep',
        name: 'Install',
        text: 'Run npm i -g indxel',
      }
    ]
  };
 
  return (
    <article>
      {/* Inject JSON-LD */}
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      
      <h1>How to set up the Indxel CLI</h1>
      <p>Follow these steps to configure your environment.</p>
      <h2>Step 1: Install</h2>
      <p>Run npm i -g indxel</p>
    </article>
  );
}

Use the schema-dts npm package. It provides complete TypeScript definitions for the entire Schema.org vocabulary, preventing typos in property names like mainEntityOfPage or datePublished.

How to fix missing structured data in Next.js Pages Router

If you are maintaining a Next.js Pages Router application, you cannot inject the script tag directly into the standard component tree without risking hydration mismatches or placement issues. You must wrap the script tag in next/head.

// pages/faq.tsx
import Head from 'next/head';
 
export default function FAQPage() {
  const faqSchema = {
    "@context": "https://schema.org",
    "@type": "FAQPage",
    "mainEntity": [
      {
        "@type": "Question",
        "name": "Which structured data types does Indxel validate?",
        "acceptedAnswer": {
          "@type": "Answer",
          "text": "Indxel validates required fields for Article, Product, FAQ, Organization, WebSite, BreadcrumbList, HowTo, and SoftwareApplication."
        }
      }
    ]
  };
 
  return (
    <>
      <Head>
        <script
          type="application/ld+json"
          dangerouslySetInnerHTML={{ __html: JSON.stringify(faqSchema) }}
        />
      </Head>
      <main>
        <h1>Frequently Asked Questions</h1>
        {/* Page content */}
      </main>
    </>
  );
}

How to fix missing structured data using the Indxel SDK

Writing dangerouslySetInnerHTML repeatedly introduces boilerplate and formatting risks. The Indxel SDK provides a StructuredData component that handles stringification, escaping, and type validation automatically.

// app/blog/[slug]/page.tsx
import { StructuredData } from '@indxel/sdk/react';
 
export default async function BlogPost({ params }: { params: { slug: string } }) {
  const post = await getPostBySlug(params.slug);
 
  return (
    <>
      <StructuredData 
        type="Article"
        data={{
          headline: post.title,
          image: [post.ogImage],
          datePublished: post.publishedAt,
          dateModified: post.updatedAt,
          author: [{
            "@type": "Person",
            name: post.authorName,
          }]
        }}
      />
      <article>
        <h1>{post.title}</h1>
        {/* Content */}
      </article>
    </>
  );
}

The StructuredData component maps the type prop to the correct Schema.org context and strictly types the data object based on Google's required and recommended fields for rich results.

How to prevent missing structured data in CI?

Relying on developers to remember JSON-LD for every new route fails at scale. You must enforce structured data presence at the CI level before code merges to the main branch.

Add npx indxel check --ci to your GitHub Actions pipeline. The --ci flag configures the CLI to exit with a non-zero status code if any rule violations are found, failing the build.

# .github/workflows/seo-check.yml
name: SEO Infrastructure Guard
 
on:
  pull_request:
    branches: [main]
 
jobs:
  validate-seo:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          
      - name: Install dependencies
        run: npm ci
        
      - name: Build application
        run: npm run build
        
      - name: Run Indxel SEO Audit
        run: npx indxel check --ci --diff

The --diff flag ensures the CLI only evaluates pages affected by the current pull request. If a developer creates a new /pricing page without structured data, the CI job fails and blocks the merge. Existing legacy pages missing structured data will not block the PR, preventing CI gridlock.

What are the edge cases for structured data implementation?

Injecting JSON-LD seems straightforward, but dynamic content and client-side rendering introduce specific failure states that search engines penalize.

Unescaped characters breaking JSON

When you populate JSON-LD fields with data from a CMS, you often include user-generated text containing quotes, backslashes, or HTML entities. If you construct the JSON string manually instead of using JSON.stringify(), you will generate invalid JSON.

Google's parser will fail silently on invalid JSON, stripping your page of rich results without notifying Search Console immediately.

Bad:

// This breaks if post.title contains a double quote, e.g., The "Best" Tool
<script type="application/ld+json">
  {`
    {
      "@context": "https://schema.org",
      "@type": "Article",
      "headline": "${post.title}"
    }
  `}
</script>

Good: Always pass the raw object to JSON.stringify(). It handles escaping quotes, line breaks, and control characters natively.

<script 
  type="application/ld+json" 
  dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaObject) }} 
/>

Empty arrays in dynamic schema

If you build an FAQPage schema dynamically from an array of questions, but the array is occasionally empty, you will output an invalid schema. Google requires the mainEntity array in FAQPage schema to contain at least one valid Question object.

// Fails validation if faqs.length === 0
const schema = {
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": faqs.map(faq => ({
    "@type": "Question",
    "name": faq.q,
    "acceptedAnswer": { "@type": "Answer", "text": faq.a }
  }))
};

You must conditionally render the entire <script> tag based on the length of the source data.

{faqs.length > 0 && (
  <script 
    type="application/ld+json" 
    dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }} 
  />
)}

Client-side injection delays

If you inject JSON-LD via a useEffect hook in a React client component ("use client"), the structured data is not present in the initial HTML payload. While Googlebot executes JavaScript and will eventually see the schema during the second wave of indexing, this delays rich result eligibility. Bingbot and other crawlers often fail to parse client-side JSON-LD entirely.

Always inject JSON-LD via Server Components or static HTML generation to ensure it is present in the raw source code.

Related rules

  • invalid-structured-data: Checks if the injected JSON-LD contains all required fields for its declared @type.
  • duplicate-structured-data: Flags pages containing multiple conflicting schemas of the same type, which confuses crawlers.

FAQ

Which structured data types does Indxel validate?

Indxel validates required fields for Article, Product, FAQ, Organization, WebSite, BreadcrumbList, HowTo, and SoftwareApplication schemas. It checks for the presence of @context, @type, and type-specific required fields like review or aggregateRating.

Is structured data a direct ranking factor?

Structured data itself is not a direct ranking factor, but it enables rich results that significantly increase click-through rates (CTR). A higher CTR signals relevance to search engines, which indirectly improves your position in the SERP.

Can I use Microdata instead of JSON-LD?

Google explicitly prefers JSON-LD and recommends it over Microdata or RDFa. JSON-LD decouples your metadata from your HTML structure, making it less prone to breaking when UI components are refactored. Indxel only validates JSON-LD.

Does JSON-LD need to be in the <head> of the document?

No, JSON-LD <script> tags can be placed anywhere in the <head> or <body> of the HTML document. Googlebot parses the entire DOM for application/ld+json tags regardless of their position. Placing it in the body is standard practice in Next.js App Router.

Frequently asked questions

Which structured data types does Indxel validate?

Indxel validates required fields for Article, Product, FAQ, Organization, WebSite, BreadcrumbList, HowTo, and SoftwareApplication. It checks for @context, @type, and type-specific required fields.

Is structured data a ranking factor?

Structured data itself isn't a direct ranking factor, but it enables rich results that significantly increase click-through rates. Higher CTR can indirectly improve rankings.

Catch this before it ships

$npx indxel check --ci
Get startedBrowse all fixes
Indxel

SEO validation that runs in your terminal and blocks bad deploys.

GitHubnpm

Product

  • Documentation
  • Pricing
  • Plus Plan
  • CI/CD Guard
  • Indexation
  • Free Tools
  • Blog

Comparisons

  • vs Semrush
  • vs Ahrefs
  • vs Moz
  • vs Screaming Frog
  • All comparisons

Integrations

  • Vercel
  • GitHub Actions
  • Netlify
  • Docker
  • All integrations

Resources

  • Frameworks & use cases
  • Next.js
  • For freelancers
  • For agencies
  • SEO Glossary

Built with care. MIT Licensed.

PrivacyTermsLegalContact