What custom JSON-LD on Shopify means
Custom JSON-LD on Shopify is structured data the merchant adds to the theme above and beyond what Shopify auto-emits. Shopify's SEO overview confirms themes auto-emit Product schema. Custom JSON-LD covers everything else — Organization, WebSite, BreadcrumbList, Article on the blog, FAQPage on the FAQ hub, plus the upgraded Product fields (gtin, hasMerchantReturnPolicy, shippingDetails) the theme's auto-emission leaves out. The injection pattern is a <script type='application/ld+json'>…</script> block placed in theme.liquid or a per-template section file.
Three placement scopes
JSON-LD on Shopify lives in one of three scopes: (1) theme.liquid <head>, for site-wide entities like Organization and WebSite that fire once per page. (2) Per-template section files (main-product.liquid, main-list-collections.liquid, main-article.liquid, page.liquid), for entities that need template-specific Liquid context. (3) Schema apps via Script Tag API or app blocks, for merchants who prefer a managed app to writing Liquid by hand.
theme.liquid placement
theme.liquid is the layout file that wraps every storefront page. Its <head> section is the canonical place for site-wide JSON-LD. Open Online Store > Themes > Actions > Edit code > Layout > theme.liquid. Find the closing </head> tag. Add the Organization and WebSite blocks immediately before </head>. Save.
Per-template section files
Modern Shopify themes (Dawn-derivative and similar) follow Online Store 2.0 architecture: each template (product, collection, article, page) is composed of sections, and the section file for the dominant section carries the template's structured data. Open Online Store > Themes > Actions > Edit code > Sections. Find main-product.liquid (for the PDP Product + BreadcrumbList block), main-list-collections.liquid (for the collection BreadcrumbList block), main-article.liquid (for the Article block). Paste your complementary JSON-LD inside the section, typically near the bottom before any closing tags.
The @graph pattern for one block per page
A Shopify page can carry multiple JSON-LD blocks (Shopify's auto-emitted Product, plus your complementary blocks for BreadcrumbList, Article, etc.), and Google's parser handles them. The cleaner pattern is one script tag per page containing a @graph array of all the entities. The @graph form deduplicates entities by @id automatically — when your Product references Organization by @id, the same Organization defined in theme.liquid resolves cleanly.
Deploying via Theme Editor, Shopify CLI, or GitHub
Three deployment paths in 2026. (1) Theme Editor: edit theme.liquid and section files directly in Online Store > Themes > Actions > Edit code. Saves are live immediately on the published theme; merchants should use a draft theme for risky changes. (2) Shopify CLI: shopify theme pull to download a local copy, edit, shopify theme push to deploy. CLI is the modern recommendation. (3) GitHub integration: connect a repo, commit theme changes, Shopify auto-deploys. Best for teams; requires the Online Store Theme version control feature.
Validation workflow
After deploying, validate one URL per template type. PDP: submit one product URL to Rich Results Test. Collection: submit one collection URL. Blog post: submit one article URL. Homepage: submit shop.url. Expected pass-fail per template: PDP detects Product + Offer + BreadcrumbList (zero errors); Collection detects BreadcrumbList; Blog post detects Article + BreadcrumbList; Homepage detects Organization + WebSite. For any errors, edit the relevant section file or theme.liquid, redeploy, re-validate.
Shopify gotchas on custom JSON-LD
Five gotchas. First: Shopify's admin code editor auto-corrects straight quotes to smart quotes — author the JSON in VS Code or another plain editor, paste into Shopify only at the final step. Second: forgetting | escape on every Liquid string variable that produces user-input text (product.title, shop.name, article.title) — apostrophes break the JSON. Third: putting Product schema in theme.liquid (where product.* is unbound off PDPs). Fourth: putting Organization schema in a section file (where it fires per-template instead of site-wide). Fifth: missing the application/ld+json mime type on the script tag — the block is then ignored by validators.