Most Shopify SEO apps lock you into their own schema. If you’ve built better JSON-LD, you can add it yourself. Below are two 100% Shopify-native methods to inject your own schema into the <head>
on specific URLs, plus a quick checklist to keep things clean.
How to set it up in Shopify
Method A (metafields – clean and scalable)
Use Shopify metafields to store JSON and output it only on pages that have it.
- Create JSON metafields
- Go to Settings → Custom data.
- Add a definition for each resource you need: Products, Collections, Pages, Articles.
- Name:
Schema JSON
- Namespace and key:
custom.schema_json
- Type:
JSON
- Name:
- Optional: add a Store (Shop) metafield the same way if you want homepage or sitewide schema.
- Paste your JSON into the metafield
- Open the product, collection, page, or article.
- Scroll to Metafields → paste your JSON-LD into
Schema JSON
→ Save. - Because the field type is JSON, Shopify validates that it’s real JSON.
- Output JSON-LD in the
<head>
- Go to Online Store → Themes → Edit code.
- Open
layout/theme.liquid
. - Place this just before
</head>
:
{%- comment -%} Product page {%- endcomment -%}
{%- if request.page_type == 'product' and product and product.metafields.custom.schema_json.value -%}
<script type="application/ld+json">
{{ product.metafields.custom.schema_json.value | json }}
</script>
{%- endif -%}
{%- comment -%} Collection page {%- endcomment -%}
{%- if request.page_type == 'collection' and collection and collection.metafields.custom.schema_json.value -%}
<script type="application/ld+json">
{{ collection.metafields.custom.schema_json.value | json }}
</script>
{%- endif -%}
{%- comment -%} Page {%- endcomment -%}
{%- if request.page_type == 'page' and page and page.metafields.custom.schema_json.value -%}
<script type="application/ld+json">
{{ page.metafields.custom.schema_json.value | json }}
</script>
{%- endif -%}
{%- comment -%} Article (blog post) {%- endcomment -%}
{%- if request.page_type == 'article' and article and article.metafields.custom.schema_json.value -%}
<script type="application/ld+json">
{{ article.metafields.custom.schema_json.value | json }}
</script>
{%- endif -%}
{%- comment -%} Optional: homepage only {%- endcomment -%}
{%- if request.page_type == 'index' and shop.metafields.custom.schema_json.value -%}
<script type="application/ld+json">
{{ shop.metafields.custom.schema_json.value | json }}
</script>
{%- endif -%}
Notes
.value
reads the parsed JSON from the metafield;| json
safely serializes it inside the<script>
.- Keep this block inside
<head>
(right before</head>
intheme.liquid
, or your theme’s head include).
Most themes already print Product/Article schema. Wrap the theme’s default schema so it won’t render when your custom metafield exists and repeat similarly for articles, collections, and pages.
{%- unless product.metafields.custom.schema_json.value -%}
{%- render 'product-schema', product: product -%}
{%- endunless -%}
Method B (per-URL conditions)
Use this when you only need a few exact URLs or special landing pages.
- In
layout/theme.liquid
, above</head>
, target paths or specific handles:
{%- comment -%} Exact path for a landing page {%- endcomment -%}
{%- if request.path == '/pages/landing-offer' -%}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "Your Custom Landing Offer",
"author": { "@type": "Organization", "name": {{ shop.name | json }} }
}
</script>
{%- endif -%}
{%- comment -%} A specific product by handle {%- endcomment -%}
{%- if request.page_type == 'product' and product and product.handle == 'my-special-sku' -%}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": {{ product.title | json }},
"sku": {{ product.selected_or_first_available_variant.sku | json }}
}
</script>
{%- endif -%}
Notes
- Use
request.path
for exact URL matches. - For resource pages, prefer handle or id checks (
product.handle
,collection.handle
, etc.). - When you inject Liquid values into JSON, always pipe through
| json
(it adds quotes and escapes properly).
Validation and check
- View source and confirm your
<script type="application/ld+json">
is inside<head>
and appears only once per intended graph. - Test the live URL in Google’s Rich Results Test and the Schema Markup Validator.
- If you see duplicates, disable the theme’s default schema for that page type using the
unless
pattern above.
Conclusion
Metafields give you full control and scale well for editors. For one-off pages, target URLs or handles with simple conditions. Keep the script in the head, validate every change, and guard against duplicates. That’s all you need to run your own custom JSON-LD in Shopify without apps.