---
title: "Render Schema.org structured data as JSON-LD"
slug: "render-schema-org-structured-data-as-json-ld"
abstract: "Learn how to render Schema.org structured data returned by the Sitecore Layout Service as JSON-LD in Content SDK Next.js apps."
---
# Render Schema.org structured data as JSON-LD

The Sitecore Layout Service can return `sitecore.context.schemas` containing an array of ready-made JSON-LD node objects, each with its own `@context` and `@type`. Content SDK renders these into a single `<script type="application/ld+json">` tag, so you don't have to hand-write structured data markup or manage serialization and escaping yourself.

## Render structured data with SitecoreHead

The updated `create-content-sdk-app` templates include a `SitecoreHead` component in `src/SitecoreHead.tsx`. It groups the scripts, styles, and structured data rendered alongside the page layout:

- `Scripts` renders the application's scripts.
- `SitecoreStyles` renders the Sitecore styles.
- `JsonLdSchema` renders the structured data from the page object.
- In the Pages Router template, `PageMetaTags` also renders page metadata tags.

This integration is included in the `nextjs`, `nextjs-app-router`, and `nextjs-app-router-cache-components` templates. Their `src/Layout.tsx` files import `SitecoreHead` from the application and pass it the page object:

```tsx
import SitecoreHead from 'src/SitecoreHead';

const Layout = ({ page }: LayoutProps): JSX.Element => {
  return (
    <>
      <SitecoreHead page={page} />
      {/* ...rest of layout... */}
    </>
  );
};
```

`SitecoreHead` is an application component supplied by the templates. It returns a React fragment, so it doesn't create an HTML `<head>` element.

### Update an existing application

Updating SDK dependencies doesn't update the files generated when you created your application. If your application doesn't include this integration, add `<JsonLdSchema page={page} />` to your existing layout as shown in the following section. You can also adopt the template's `SitecoreHead` structure by moving your existing scripts and styles into `src/SitecoreHead.tsx`, along with `JsonLdSchema` and, for Pages Router apps, `PageMetaTags`.

Render `JsonLdSchema` once per page. If `SitecoreHead` already includes it, you don't need to add another instance to your layout.

## Render structured data with JsonLdSchema

Use the `JsonLdSchema` component from `@sitecore-content-sdk/nextjs` (or `@sitecore-content-sdk/react`) and pass it your page object:

```tsx
import { JsonLdSchema } from '@sitecore-content-sdk/nextjs';

const Layout = ({ page }: LayoutProps): JSX.Element => {
  return (
    <>
      <JsonLdSchema page={page} />
      {/* ...rest of layout... */}
    </>
  );
};
```

`JsonLdSchema` accepts the following props:

| Prop   | Type          | Description                                                        |
| ------ | ------------- | -------------------------------------------------------------------- |
| `page` | `Page \| null` | Page object, for example `props.page` on a Sitecore page component. |

The component is framework-agnostic and doesn't need to live inside `<head>` for its structured data to be discovered by crawlers, so you can render it anywhere in the tree — nested inside `next/head`'s `<Head>` in a Pages Router layout, or directly in a Next.js App Router Server Component.

`JsonLdSchema` renders nothing in the following cases:

- There are no schemas on `page.layout.sitecore.context.schemas`.
- `page.mode.isNormal` is `false` — for example while in Pages/Experience Editor, Preview, or Design Library — so structured data isn't injected around unpublished or editing chrome.

## Schema serialization

All schema node objects from `sitecore.context.schemas` are serialized into a single script as one JSON array, rather than one `<script>` tag per schema. Entries that aren't valid objects (`null`, `undefined`, arrays, or other non-object values) are filtered out before serialization.

> [!note]
> The serialized JSON is escaped so that characters such as `<`, `>`, `&`, and `/` can't be used to break out of the `<script>` tag (for example, via a `</script>` sequence embedded in a field value). This doesn't change the data and only affects how it's embedded in the HTML response.

## Using getJsonLdSchemas directly

For rendering layers other than React (or for custom output), use the framework-agnostic `getJsonLdSchemas` function from `@sitecore-content-sdk/content/layout` (also re-exported from `@sitecore-content-sdk/nextjs`):

```ts
import { getJsonLdSchemas } from '@sitecore-content-sdk/content/layout';

const script = getJsonLdSchemas(context.schemas);
```

`getJsonLdSchemas` returns an `HTMLScript` object, or `null` if there are no valid schemas to render:

```ts
type HTMLScript = {
  [key: string]: unknown;
} & Pick<HTMLScriptElement, 'type'> & {
    innerHTML: string;
  };
```

`innerHTML` is already escaped and safe to assign directly, for example via `dangerouslySetInnerHTML` in React.
