To add JSON-LD structured data, put a <script type="application/ld+json"> tag in each page's HTML containing schema.org data that describes that page: who runs the site, what the page is, and where it sits in the site. Use one main type per page, link every page to the same Person or Organization with an @id, and test it in Google's Rich Results Test. Only describe what visitors can actually see.
Every page on this site carries its own JSON-LD, generated from the same data the page renders. Below is the setup I use, the mistakes I have fixed on my own site, and a free Schema Markup Generator if you would rather not write JSON by hand.
What is JSON-LD structured data?
Structured data is a description of a page written for machines. JSON-LD is the format: a block of JSON inside a script tag, using the shared schema.org vocabulary. Search engines read it to understand that a page is an article by a particular person, a service with a list of deliverables, or a business with an address and opening hours.
It does two jobs. It can make a page eligible for rich results, such as product prices or breadcrumbs in Google. And it gives answer engines, such as AI Overviews, ChatGPT and Perplexity, clean facts they do not have to infer from your prose.
Which schema types should each page use?
One main type per page, plus breadcrumbs. This is the map I use on mohitgoyal.in:
| Page | Main type | Also included |
|---|---|---|
| Home | Person | WebSite and ProfilePage, linked to the Person |
| Project page | CreativeWork | BreadcrumbList |
| Projects or tools listing | ItemList | BreadcrumbList |
| Service page | Service, with an OfferCatalog of deliverables | BreadcrumbList |
| Case study | Article, with the client as an Organization | BreadcrumbList |
| Blog post | BlogPosting | BreadcrumbList, and FAQPage when the post has an FAQ |
| Free tool page | WebApplication | HowTo, FAQPage and BreadcrumbList |
For other kinds of site the same idea applies. A restaurant or clinic home page uses LocalBusiness or a more specific subtype, a store uses Product on product pages, and an event page uses Event.
Two rules keep the map honest. First, the main type should match what a visitor would call the page: if they would call it an article, it is an Article or BlogPosting. Second, breadcrumbs should mirror the real navigation. If the breadcrumb trail in the markup says Home, Blog, Post, the page should show the same path, and every URL in it should return a 200.
How do you link pages together with @id?
Give the main entity an @id, a stable URL-like identifier, and refer to it from every other page. On this site the Person has the id https://mohitgoyal.in/#person. The WebSite says its publisher is that id, the ProfilePage says its main entity is that id, and every blog post and project names the same Person as author.
The result is one connected graph instead of dozens of disconnected snippets. An engine reading any page learns that it belongs to the same site and the same person as every other page. For a business, use an Organization with an @id in the same way.
What does a good JSON-LD block look like?
When a page has several entities, wrap them in an @graph array. Here is a trimmed version of the markup on a blog post:
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "BlogPosting",
"@id": "https://mohitgoyal.in/blog/example#post",
"headline": "Post title",
"datePublished": "2026-10-25",
"author": { "@type": "Person", "@id": "https://mohitgoyal.in/#person", "name": "Mohit Goyal" },
"isPartOf": { "@id": "https://mohitgoyal.in/#website" }
},
{
"@type": "BreadcrumbList",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "https://mohitgoyal.in/" },
{ "@type": "ListItem", "position": 2, "name": "Blog", "item": "https://mohitgoyal.in/blog" }
]
}
]
}
One detail worth copying: when the JSON is printed inside a script tag, I escape every < as <. That way no text from the content, however odd, can close the script tag early and inject markup.
What mistakes should you avoid?
Wrong data is worse than no data
My home page used to output an FAQPage where every quick question carried the same About text as its answer. That is low-quality markup, so I removed it. If the data is not true and distinct, leave the type out.
The other mistakes I see most often:
- Marking up what is not on the page. Reviews, prices or FAQs that visitors cannot see break Google's guidelines and can cost you rich results.
- Client-side injection. JSON-LD added by a script after load may never be seen by crawlers that do not run JavaScript. Render it on the server.
- Hand-written blocks that drift. A price or address changes on the page but not in the markup. Generate the JSON-LD from the same data the page uses, so it cannot go stale.
- Several unrelated main types on one page. Pick what the page is about. A blog post is a BlogPosting, not also a Product and a LocalBusiness.
- Invented ratings. Never add an aggregateRating you cannot back up with reviews shown on the page.
How do you add JSON-LD in Next.js or WordPress?
- 1
Decide the main type per template
Use the table above. Templates, not individual pages, are what you mark up.
- 2
Generate from page data
In Next.js or TanStack Start, build the object from the same data the page renders and print it in a script tag on the server. In WordPress, use your SEO plugin's schema settings or a small plugin that reads the post's fields.
- 3
Link to your main entity
Every page refers to the same Person or Organization @id.
- 4
Test before launch
Run each template through the Rich Results Test and the Schema Markup Validator.
- 5
Watch Search Console
After launch, check the enhancement reports for errors and warnings, and fix them at the template level.
For a single page, such as a local business home page or one event, the free Schema Markup Generator writes the block for you from a plain description and never invents values. For the rest of the technical setup on a Next.js site, see my technical SEO checklist for Next.js.
Does structured data help with AI answers?
It helps engines get your facts right, which is the first step to being quoted. Structured data is one part of that. The others are server-rendered pages, answer-first writing and consistent facts across your site, covered in how to get cited in AI Overviews.
I add schema at the template level on every site I build, and I audit and fix it on existing sites through my technical SEO and Core Web Vitals service.



