# Each listing page has valid JSON-LD with correct Schema.org type

- **Pattern:** `ab-001015` (`directory-listing-schema.structured-data.json-ld-valid`)
- **Severity:** critical
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001015
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001015)

## Why it matters

Missing or malformed JSON-LD blocks Google's rich results pipeline entirely. When a directory listing omits a valid `<script type="application/ld+json">` block with the correct schema-org LocalBusiness or Place `@type`, Google cannot generate rich snippets — star ratings, addresses, and hours never appear in SERPs. Listings that lack structured data are effectively invisible in the competitive layer where rich results dominate click-through rates. A JSON-LD object stored in an HTML `data-` attribute rather than the script's text content is the canonical hallucination failure for AI-generated code: it compiles and renders fine, but search engine parsers skip it completely.

## Severity rationale

Critical because misconfigured or absent JSON-LD eliminates the directory's structured-data eligibility entirely, directly suppressing rich-result visibility for every affected listing.

## Remediation

Add a reusable `JsonLd` component that injects the schema as the script element's inner text, not an HTML attribute. The `dangerouslySetInnerHTML` pattern is correct here — the input is always `JSON.stringify()` of a controlled object.

```tsx
// components/json-ld.tsx
export function JsonLd({ schema }: { schema: object }) {
  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
    />
  );
}
```

Use it on every listing page template: `<JsonLd schema={{ "@context": "https://schema.org", "@type": "LocalBusiness", ...listing }} />`. Validate the output with Google's Rich Results Test.

## Detection

- **ID:** `json-ld-valid`
- **Severity:** `critical`
- **What to look for:** Enumerate every page that displays a business or listing. For each page, examine listing page templates or components for `<script type="application/ld+json">` tags. Look for: (1) a `<script type="application/ld+json">` element whose inner HTML (text content) contains a JSON object with `@context` and `@type` fields, (2) a dedicated `JsonLd` or `StructuredData` component that renders such a script. The JSON must be embedded as the script element's text content — not stored only in an HTML attribute like `data-json` or `data-schema`, which search engines cannot read. Accept: `dangerouslySetInnerHTML` pattern, direct JSX string child, or server-rendered inline script. Reject: JSON-LD object stored only in a data attribute with no text content. Validate the `@type` matches an appropriate Schema.org type for the listing domain (LocalBusiness, Place, ServiceArea, Organization, etc.).
- **Pass criteria:** Every listing page contains a valid JSON-LD block with a correct Schema.org type matching the listing content domain. The schema JSON is embedded as the script element's text content (visible to search engine parsers) — 100% of listing pages must include syntactically valid JSON-LD with @type and @context fields. Report the count: "X listing pages found, all Y include valid JSON-LD."
- **Fail criteria:** JSON-LD is missing; JSON is syntactically invalid; `@type` is incorrect or generic (e.g., `"Thing"`); JSON content is stored only in an HTML data attribute rather than the script's text content.
- **Skip (N/A) when:** Never — structured data is essential for directory listings.
- **Cross-reference:** The `required-fields` check verifies that all mandatory schema fields are populated.
- **Detail on fail:** Specify the issue. Example: `"No JSON-LD found on listing pages"` or `"JSON-LD present but uses @type 'Thing' instead of LocalBusiness or Place"` or `"JSON-LD has syntax error: missing closing brace"` or `"JsonLd component stores schema in data-json HTML attribute only — text content is empty, making it invisible to search engines"`
- **Remediation:** Add valid JSON-LD to listing page templates. The schema must be embedded as the script tag's text content. A reusable component pattern:

  ```tsx
  // components/json-ld.tsx — inject schema as inner HTML text content
  export function JsonLd({ schema }: { schema: object }) {
    const schemaString = JSON.stringify(schema);
    // The __html key injects content as raw text — safe here because
    // schema is always JSON.stringify() of a hardcoded or sanitized object
    return (
      <script
        type="application/ld+json"
        // Use innerHTML injection so text content is readable by search engines
        {...{ dangerouslySetInnerHTML: { __html: schemaString } }}
      />
    );
  }
  ```

## External references

- schema-org LocalBusiness
- schema-org Place

Taxons: findability

HTML version: https://auditbuffet.com/patterns/ab-001015
