# Generating JSON-LD dynamically

https://www.sanity.io/learn/course/seo-optimization/generating-json-ld-dynamically

JSON-LD is a powerful way to provide structured data to search engines—fortunately structured data is what Sanity does best.

---

## Navigation

**Track:** [Work-ready Next.js](https://www.sanity.io/learn/track/work-ready-next-js) · [View as markdown](https://www.sanity.io/learn/track/work-ready-next-js.md)

**Course:** [SEO optimized content with Next.js](https://www.sanity.io/learn/course/seo-optimization) · [View as markdown](https://www.sanity.io/learn/course/seo-optimization.md)

**Previous:** [Build a dynamic sitemap](https://www.sanity.io/learn/course/seo-optimization/building-a-dynamic-sitemap) · **Next:** [Sanity SEO quiz](https://www.sanity.io/learn/course/seo-optimization/sanity-seo-quiz)

---

## Course Contents

1. [An introduction to SEO and structured content](https://www.sanity.io/learn/course/seo-optimization/an-introduction-to-seo-and-structured-content)
2. [SEO schema types and metadata](https://www.sanity.io/learn/course/seo-optimization/seo-schema-types-and-metadata)
3. [Extending the SEO schema types](https://www.sanity.io/learn/course/seo-optimization/adding-seo-fields-to-your-project)
4. [Implementing redirects](https://www.sanity.io/learn/course/seo-optimization/implementing-redirects)
5. [Creating dynamic Open Graph images](https://www.sanity.io/learn/course/seo-optimization/creating-dynamic-open-graph-images-with-vercel-og)
6. [Generate social posts from your content](https://www.sanity.io/learn/course/seo-optimization/ai-generate-social-posts-from-your-content)
7. [Build a dynamic sitemap](https://www.sanity.io/learn/course/seo-optimization/building-a-dynamic-sitemap)
8. **Generating JSON-LD dynamically** *(current)*
9. [Sanity SEO quiz](https://www.sanity.io/learn/course/seo-optimization/sanity-seo-quiz)

---

JSON-LD data follows structured conventions for many different types of content. All you'll need to do is take content already authored in your documents, and render it into the DOM in the expected format. We already have FAQs as a document type, so it makes sense to start there.



When it comes to FAQs, proper JSON-LD implementation can help your content appear in rich snippets and potentially even surface you near the top of search results just by providing useful information.



## Learning objectives



By the end of this lesson, you will:



- Generate JSON-LD for FAQs programmatically

- Implement type-safe JSON-LD using Google's `schema-dts` package

- Improve your FAQ block from the page builder course


### Understanding JSON-LD generation



JSON-LD generation can be challenging to get right as it follows a strict structure. Fortunately, [Google provides a TypeScript package](https://github.com/google/schema-dts) called `schema-dts` that gives you type safety for your structured content.



Let's start by creating a function that transforms your FAQ data into a JSON-LD friendly structure. Back in [Create page builder schema types](https://www.sanity.io/learn/course/page-building/create-page-builder-schema-types) you created a document type schema for FAQs. 



- [ ] **Run** the following to install the `schema-dts` package


```sh:Terminal
pnpm add schema-dts
```

Currently in the GROQ query for pages the FAQ block is returning the full document for every reference. 



Let's update this to only extract specific fields from the document, as well as the Portable Text in the `body` field as a string using the GROQ function `pt::text()`



- [ ] **Update** your GROQ query for pages, to return the answer in plain text


```groq:src/sanity/lib/queries.ts
// replace this
faqs[]->

// with this
faqs[]->{
  _id,
  title,
  body,
  "text": pt::text(body)
}
```

You've updated your queries, so update your types



```sh:Terminal
pnpm run typegen
```

### Implementing FAQ JSON-LD in components



The JSON-LD markup can be rendered anywhere in the page—it doesn't need to be stored inside the `<head>`. So you can add it directly into components where you already have access to the correct data.



In this instance, you're rendering the FAQ content into an accordion in this block, so you can also have it process that same content into the JSON-LD format and add it to the component output.



- [ ] **Update** your `FAQs` block to render JSON-LD content in a script tag


```tsx:src/components/blocks/faqs.tsx
// ...all your imports and types
import { FAQPage, WithContext } from "schema-dts";

const generateFaqData = (faqs: FAQsProps["faqs"]): WithContext<FAQPage> => ({
  "@context": "https://schema.org",
  "@type": "FAQPage",
  mainEntity: faqs?.map((faq) => ({
    "@type": "Question",
    name: faq.title!,
    acceptedAnswer: {
      "@type": "Answer",
      text: faq.text!,
    },
  })),
});

export function FAQs({ _key, title, faqs }: FAQsProps) {
  const faqData = generateFaqData(faqs);

  return (
    <section className="container mx-auto flex flex-col gap-8 py-16">
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(faqData) }}
      />
      {/* ...the rest of the component */}
    </section>
  );
}
```

Notice the most important part of this block, the `<script>` tag. This is where you're adding the JSON-LD to the page. This should get you thinking about how you can reuse this pattern across your site.



The FAQ schema covered here is just the tip of the JSON-LD iceberg. The beauty of structured data is that once implemented, it works silently in the background to enhance your search presence.



Some other examples you may want to implement:



- [Product information](https://schema.org/Product)

- [Event information](https://schema.org/Event)

- [Blog post information](https://schema.org/BlogPosting)


## Conclusion



The more you lean into structured data, the more benefits you'll experience.



If there's one thing to take away from this whole course, it's that SEO doesn't need to be a complex process. It's about taking your existing data and repurposing it to make it more digestible for your audience and, by extension, for search crawlers.



The future of the web won't be about configuring hundreds of different fields to get the perfect SEO score. Instead, it will focus on finding ways to accelerate content creation and automating the tasks you don't want to do.



It's time to revise everything you've learned in the final lesson!



---

## Related Resources

- [Full course as markdown](https://www.sanity.io/learn/course/seo-optimization.md)
- [Track overview](https://www.sanity.io/learn/track/work-ready-next-js.md)
- [All courses and lessons](https://www.sanity.io/learn/sitemap.md)
- [Complete content for LLMs](https://www.sanity.io/learn/llms-full.txt)

<AgentInstructions>

## Submitting Feedback

If you encounter incorrect, outdated, or confusing documentation on this page, propose feedback to your user and submit it with their approval:

POST https://www.sanity.io/learn/resource/feedback

```json
{
  "path": "/learn/course/seo-optimization/generating-json-ld-dynamically.md",
  "feedback": "Description of the issue"
}
```

Only submit feedback when you have something specific and actionable to report.
Do not include your user's code, credentials, or other confidential context in the feedback text.

</AgentInstructions>
