How to Add Schema Markup to Any Website (With or Without Plugins)
The short answer: you add schema markup by pasting a block of JSON-LD (JavaScript Object Notation for Linked Data) into your page's HTML inside a <script type="application/ld+json"> tag. Google recommends JSON-LD over the older Microdata and RDFa formats, and every rich result Google supports can be described with it. So this whole guide is really about one skill: getting a valid JSON-LD block onto your page, once, without duplicating it.
If you want the background on what structured data is and why it exists, read our structured data guide first, and our breakdown of which schema types are worth using. This post assumes you already know you want, say, Article or FAQ markup and just need it live on the page. The fastest way to produce the block itself is our Schema Markup Generator, which outputs valid JSON-LD you can copy straight into any of the platforms below.
What a schema block actually looks like
Here is a complete, minimal Article block. Every schema block follows this same shape: a script tag, an @context of https://schema.org, a @type, and then the properties for that type.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "How to Add Schema Markup to Any Website",
"image": "https://example.com/img/schema-cover.jpg",
"datePublished": "2026-04-20T08:00:00+00:00",
"dateModified": "2026-04-20T08:00:00+00:00",
"author": {
"@type": "Person",
"name": "Jordan Ellis",
"url": "https://example.com/authors/jordan-ellis"
},
"publisher": {
"@type": "Organization",
"name": "Example Media",
"logo": {
"@type": "ImageObject",
"url": "https://example.com/img/logo.png"
}
}
}
</script>
That is the entire deliverable. The rest of adding schema is deciding where this block lives on your specific platform.
Where to paste it: raw HTML sites
If you hand-edit HTML files or use a static site generator, put the script tag in the <head> of the page it describes. It also works in the <body>, and Google reads it in either place, so if your template only lets you inject into the body, that is fine. The one rule that matters: the block must be in the HTML that gets served, not added later by JavaScript. More on that in the mistakes section.
Put page-specific markup (the Article, the Product, the Recipe) on that page only. Put site-wide markup (your Organization or WebSite block) in a shared header template so it appears on every page once.
Where to paste it: WordPress
You have two honest options on WordPress, and the plugin-versus-manual choice depends on how much you already run.
If your site already uses an SEO plugin like Yoast, Rank Math, or The SEO Framework, it is probably already outputting schema. Yoast emits a connected graph (WebSite, WebPage, Organization, and often Article) automatically. Before you add anything, view your page source and search for application/ld+json. You may find you already have Article markup and just need to fill in author and publisher details in the plugin settings rather than paste anything.
If you want to add a type the plugin does not cover (a HowTo, a specific Event), paste the JSON-LD into the page with a block. In the block editor, add a Custom HTML block and drop the full <script> tag inside it. That renders the markup in the post body, which Google reads fine. Avoid pasting it into a Paragraph block, which will escape the angle brackets and print the code as visible text.
The manual route (editing header.php in your theme) works but ties the markup to your theme, so it disappears the day you switch themes. Prefer the Custom HTML block or a dedicated snippet plugin.
Where to paste it: Shopify
Shopify templates are Liquid files. Open the relevant template (product.liquid or a section file like sections/main-product.liquid) in the theme code editor under Online Store > Themes > Edit code. Paste your <script type="application/ld+json"> block into the template, and use Liquid variables so the values fill in per product:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": {{ product.title | json }},
"image": {{ product.featured_image | image_url: width: 1200 | json }},
"description": {{ product.description | strip_html | json }},
"offers": {
"@type": "Offer",
"price": {{ product.price | divided_by: 100.0 | json }},
"priceCurrency": {{ cart.currency.iso_code | json }}
}
}
</script>
The | json filter is doing real work there: it escapes quotes and special characters in your product titles and descriptions so they do not break the JSON. Skip it and a product named 6" Chef's Knife will produce invalid markup. Note that many Shopify themes already ship Product schema in their product template, so check the source before adding a second block.
Where to paste it: Next.js and React
In Next.js, render the script tag as part of the page component. The clean way in the App Router is a plain script tag with the JSON stringified:
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleSchema) }}
/>
Because Next.js server-renders this, the JSON-LD is in the initial HTML response, which is what you want. Do not build the object and inject it in a useEffect, because that runs only in the browser after hydration, and some crawlers will not see it.
The paste mistakes that break it
Four things account for most broken markup we see in structured data tests:
Client-side injection. This is the big one. If your JSON-LD is added by JavaScript after page load (a tag manager, a useEffect, a third-party widget), the Rich Results Test may still pass because Googlebot renders JavaScript, but you are relying on the render pass and losing time-to-index. Server-render the block whenever you can.
Escaping. JSON is strict. A stray unescaped double quote inside a headline or description breaks the entire block, and the error is often just "syntax error" with no useful line number. Let a generator or your platform's JSON filter handle escaping rather than typing quotes by hand.
Duplicate types from plugin plus theme. If your SEO plugin outputs Article markup and your theme also outputs Article markup, you get two Article blocks describing the same page. This is not fatal, but it is confusing and can send conflicting values (two different datePublished dates). Pick one source. View source, count your application/ld+json blocks, and remove the redundant one.
Markup that does not match the page. Google's structured data policies require the marked-up content to be visible to users on that page. A Product block with a price the page does not show, or FAQ markup for questions that are not on the page, is a policy violation that can earn a manual action. The schema describes the page; it does not replace it.
A quick workflow
Generate the block with the Schema Markup Generator, paste it into the right place for your platform, view the page source to confirm it is in the served HTML, then validate it. If you are unsure your content is otherwise sound, run the page through our free SEO analyzer and work through our on-page SEO checklist while you are in there.
Frequently Asked Questions
Does schema markup go in the head or the body of the page?
Either works. Google reads JSON-LD in both the <head> and the <body>. Use whichever your platform makes easier. What matters is that the block is in the HTML that gets served to crawlers, not added later by JavaScript.
Do I need a plugin to add schema markup?
No. Schema is just a JSON-LD script tag, and you can paste it directly into raw HTML, a Shopify Liquid template, or a Next.js component. Plugins are convenient on WordPress because they generate and maintain the markup for you, but they are not required.
Can I have more than one schema block on a page?
Yes. A page can carry several JSON-LD blocks (for example an Organization block and an Article block), and Google reads all of them. The problem to avoid is two blocks describing the same thing with conflicting values, which usually happens when a plugin and a theme both output the same type.
Why does my schema pass the test tool but not show rich results?
Passing validation means the syntax is correct and the page is eligible, not that Google will show a rich result. Rich results are discretionary: Google decides per query and per site based on quality and trust. Eligibility is the requirement; display is never guaranteed.
What is the difference between JSON-LD, Microdata, and RDFa?
They are three formats for the same structured data. JSON-LD keeps the data in a single script block separate from your HTML, while Microdata and RDFa annotate your existing HTML tags with attributes. Google supports all three but recommends JSON-LD because it is easier to add and maintain.