Home » Articles » Shopify » How to manually add custom schema markup in Shopify

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.

  1. 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
  • Optional: add a Store (Shop) metafield the same way if you want homepage or sitewide schema.
  1. 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.
  1. 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> in theme.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

    1. View source and confirm your <script type="application/ld+json"> is inside <head> and appears only once per intended graph.
    2. Test the live URL in Google’s Rich Results Test and the Schema Markup Validator.
    3. 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.

    Unlimited technical support for website owners

    Starting from $1,240 per month.

    Just posted

    Wave

    Enjoy our articles? Join our free list and get more.

    Sign Up

    Book a Discovery Call