Skip to content

JSON-LD present in raw HTML

What the scan checks: it parses the raw HTML response (before any JavaScript runs) and looks for at least one valid <script type="application/ld+json"> block. Zero blocks in the raw HTML is a fail. Blocks that are present but do not parse count as invalid and are dropped.

It also parses the rendered page. If your raw HTML has no JSON-LD but the rendered DOM does, the scan says so explicitly: “schema is injected client-side — invisible to non-rendering AI crawlers.” That is a different problem from having no schema, and it needs the opposite fix, so the two are never reported as the same thing. The evidence tells you how many blocks appeared after JavaScript ran and what types they were.

JSON-LD is how you tell an engine, in machine-readable form, that this page is an Article by a named author, that this is your Organization, that these are your FAQ pairs. It is the highest-signal structured data for AI answers. But if your schema is injected client-side (by Google Tag Manager, a React useEffect, or a schema plugin that writes it after load), a non-rendering crawler never sees it. The markup has to be in the document the server sends.

Emit the JSON-LD server-side, in the HTML head or body of the initial response:

<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"@id": "https://example.com/#org",
"name": "Example Inc",
"url": "https://example.com"
}
</script>
  • Next.js: render the <script> inside a Server Component or the route’s generateMetadata, never a "use client" component.
  • WordPress: use a server-side schema plugin (Rank Math, Yoast, Schema Pro) rather than one that injects via JavaScript.
  • Nuxt / SvelteKit: the server-rendered head, not an onMounted or onMount hook.
  • Webflow: the <head> Custom Code field on the page or site.
  • Avoid GTM for schema. Tag Manager writes the tag client-side, so bots that do not run JS miss it.

If the scan says your schema is client-side only

Section titled “If the scan says your schema is client-side only”

You already have the markup. Do not write new schema — move the markup you have into the server response. The three causes, in the order we see them:

  1. Google Tag Manager is firing a Custom HTML tag containing the JSON-LD. Move that block into your template.
  2. A React/Vue component writes the <script> in an effect after mount. Render it during SSR instead.
  3. A schema plugin running client-side. Most have a server-side mode or a server-side equivalent; switch to it.

Then verify from the command line, because a browser will always show you the schema whether it is server-rendered or not:

Terminal window
curl -s https://example.com/ | grep -c 'ld+json'

If that prints 0, no non-rendering crawler can see your structured data, whatever the browser’s DevTools show you.

Also validate that the block actually parses: paste it into the Schema.org validator or Google’s Rich Results Test. Invalid JSON-LD (a trailing comma, an unescaped quote) is silently ignored by every consumer, so a block that is present but broken helps you as little as no block at all.

If your HTML carries the older formats (itemscope/itemtype, or typeof/vocab), the scan notes it and stops there — we do not parse or grade them. You do not need to strip them. Leave the legacy markup in place and layer JSON-LD on top; there is no penalty for redundant schema as long as the entities agree.