# Show a Shopify metafield on your product page and in JSON-LD

> How a Shopify metafield reaches the live product page and its JSON-LD: the definition, the template, dynamic source or Liquid, app embed, and the HTML proof.

URL: https://clione.ai/guides/metafield-to-published-page/
Lang: en

You put a T-shirt's material in one of its [metafields](/glossary/shopify-metafields/), save, open the [product page](/glossary/product-page/) and it isn't there. Or it's on the page and not in the [JSON-LD](/glossary/json-ld/). Both are normal on Shopify, because **saved data and published data live in different places**: the first in the admin, the second in the HTML your store sends. This guide follows one value of a product metafield from its definition to the source code of the live product page, with the rules as Shopify writes them, and at every step it tells you how to check that it happened.

## Where the data travels, and where it gets lost

A product metafield's value covers four stretches before a person or a [web crawler](/glossary/web-crawler/) reads it:

| Stretch            | What has to happen                                             | Where it usually gets lost                                                                        | Where you check it                 |
| ------------------ | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ---------------------------------- |
| 1. Saved           | The product has a value in a metafield that has its definition | The product is empty, or the data sits in another namespace                                       | The product, in the admin          |
| 2. In the template | A template in the published theme reads it                     | Connected in another template, in another theme, or nowhere                                       | The theme editor or the theme code |
| 3. In the HTML     | The value is written into the server's response                | An app renders it and its [app embed](/glossary/shopify-app-embed/) is off, or JavaScript adds it | The page source                    |
| 4. In the JSON-LD  | The value is inside the product markup                         | The theme's markup doesn't include it                                                             | The `application/ld+json` block    |

The first three decide whether the data is visible; the fourth, whether it goes into the [structured data](/glossary/structured-data/). They are independent: data can be visible without being in the markup, and it can be in the markup without being visible, which is exactly what Google asks you to avoid. Don't mark up content that the page's readers can't see.[^google-sd-policies]

## Step 1: check the definition and the value

A metafield has an identifier, which is the namespace plus the key (`custom.material`, for example), a value, and a type, which says what kind of data it is and how it gets interpreted. The metafield's definition comes first, and the definition is what provides validation, the editor in the admin and access control. Shopify also offers standard definitions for common cases, such as ingredients or care instructions.[^shopify-metafields-dev]

Open the definition under **Custom data** in your admin settings and **write down three exact things: the namespace, the key and the type**. You'll need them in steps 3 and 6. Then open the product and check that the metafield has a value: a product with no value prints nothing, however well it's connected.

The namespace also tells you whose data it is. Metafields the merchant creates use unreserved namespaces, such as `custom`; an app's go in its reserved namespace, `$app`, and Shopify's usually start with `shopify--`.[^shopify-metafields-dev] If an app wrote the value in its own namespace, check first whether that app is the one publishing it: that's step 4.

One setting you don't need to touch: the definition's Storefront API access. Shopify says that permission is for [headless](/glossary/headless-commerce/) and custom storefronts, and that **it doesn't affect [Liquid](/glossary/shopify-liquid/) templates: from Liquid, metafields are always accessible**.[^shopify-metafields-dev] Turn it on only if your storefront is headless.

## Step 2: find out which template and which theme render the page

When you edit a template, the change reaches every page that uses that template, and only those.[^shopify-help-templates] A product can be assigned an alternate template instead of the default product template: you choose it on the product itself, in the **Theme template** drop-down.[^shopify-help-templates] Those templates are saved as `product.<suffix>.json`, and you can view any product with one of them by adding `?view=<suffix>` to its URL.[^shopify-alternate-templates] If you work in the code, Liquid's `template` object returns the suffix of the template in use, or `nil` if it's the default.[^liquid-template]

Check which theme you're editing, too. JSON templates are theme files that store which sections render and with which settings,[^shopify-json-templates] so **a connection made in a copy of the theme is not in the published theme** until you publish that copy.

Before moving on, have two answers ready: which template the page you want to fix uses, and whether the theme you have open is the published one.

## Step 3: connect the metafield in the template

There are two roads, and your theme's type decides which ones you have.

**With a dynamic source, from the theme editor.** A dynamic source connects a section or block setting to a piece of store data, such as a [product attribute](/glossary/product-attributes/) or a metafield's value.[^shopify-dynamic-sources] It needs a theme that supports JSON templates.[^shopify-help-sections] Online Store 2.0 themes have them; older themes, which Shopify calls vintage, don't.[^shopify-theme-versions] To find out which one you have, Shopify suggests opening the theme editor, going to the default product template and looking for the button to add a section below the list of sections: if it's there, the theme supports sections on every page.[^shopify-theme-versions]

The steps, according to Shopify: open the theme editor, choose the template that holds the section or block, click the **Dynamic source** button on the setting you want to fill, pick the metafield and save.[^shopify-help-sections] Their example is a candle store that adds a text block to the main section of the product template and connects it to the burn time metafield. When the connection icon shows a **+**, you can add up to 20 metafields to a single template, one at a time.[^shopify-help-display]

Not every metafield fits every setting. This is the compatibility Shopify publishes, summarized:[^shopify-dynamic-sources]

| Block setting                                                   | Metafield types it accepts                                                                                          |
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| Text (`text`)                                                   | Single-line text and its list, integers and decimals, date, date and time, weight, volume, dimension, rating, money |
| Rich text (`richtext`)                                          | The same, plus multi-line text, rich text and link                                                                  |
| Image (`image_picker`)                                          | File reference                                                                                                      |
| Color (`color`)                                                 | Color                                                                                                               |
| URL (`url`)                                                     | URL                                                                                                                 |
| Product and product list                                        | Product reference and list of product references                                                                    |
| [Metaobject](/glossary/shopify-metaobject/) and metaobject list | A [metaobject](/glossary/shopify-metaobject/) reference and its list, from the same definition the setting asks for |

Two practical consequences. **Multi-line text doesn't go into a plain text setting**: it needs a rich text one (`richtext`). And anything not in the table, such as a boolean or a JSON, can't be connected from the editor: for the types the editor doesn't support, Shopify points you to editing the theme code.[^shopify-dynamic-sources][^shopify-help-sections]

**With Liquid, in the theme code.** This is the road for vintage themes, for types with no compatible setting, and for anyone who wants control over the exact HTML. Shopify warns you to edit theme code only if you have coding experience.[^shopify-help-display] In Liquid, you read a metafield with `product.metafields.namespace.key`, and its `value` property returns the data formatted by its type: a string for text, a number for numbers, a measurement object for weight.[^liquid-metafield] A fictitious example, for the product section:


```liquid
{% if product.metafields.custom.material %}
  <p class="material">Material: {{ product.metafields.custom.material.value }}</p>
{% endif %}
```


Three details from the Liquid reference that save you an afternoon. If your metafield's key is `size`, `first` or `last`, read it with square brackets (`product.metafields.custom["size"]`): with a dot, if the metafield doesn't exist, Liquid applies the filter of the same name, and `size` returns how many metafields there are instead of nothing. The legacy types `integer`, `json_string` and `string` don't have these properties and don't accept the metafield filters.[^liquid-metafield] And if you'd rather not write the HTML by hand, the `metafield_tag` filter outputs a different element depending on the type, such as a `<time>` for a date.[^liquid-metafield-tag]

Whichever road you take, Liquid is resolved on the server: Shopify recommends rendering the initial content with Liquid and HTML, so the server responds with the complete HTML and the browser shows it as soon as it arrives.[^shopify-render-server] That's [server-side rendering](/glossary/server-side-rendering/), and it's why a value printed this way is in the source code any crawler downloads.

## Step 4: if an app publishes the data, turn on its app embed

An app gets into your theme in three ways: as an app block, which you place in a section; as an app embed, which adds code without a fixed spot on the page; or by injecting code directly into the theme.[^shopify-help-apps] Of the two block types, the one that goes into the page head is the app embed: Shopify inserts app embeds before the closing `</head>` or `</body>` tag, and **they come deactivated after you install the app**. You turn them on in the theme editor, under **App embeds** in the theme settings.[^theme-app-ext-config]

When you turn one on, watch for two things Shopify's help puts in writing. If you sell in several markets and don't pick one from the editor's **Market** drop-down, the change applies to the store's default settings. And if you change your published theme, the apps you activated in the old one won't be active in the new one: you have to turn them back on.[^shopify-help-apps] App embeds work with any theme version; app blocks only work with compatible themes.[^shopify-help-apps]

If you'd rather check it in the theme code: an app embed is added to `config/settings_data.json` the first time it's activated, and if it's deactivated later, it stays there with `"disabled": true`.[^theme-app-ext-config]

## Step 5: find out where your JSON-LD comes from

The [Product](/glossary/product-schema/) markup on a Shopify product page can come from several places, and it's worth knowing which one before you ask why it doesn't carry your metafield:

- **The theme.** In the two Shopify themes whose code we read on GitHub on October 7, 2026, the product markup is written by the product section in a single line: `product | structured_data` in Dawn's `main-product.liquid` and `closest.product | structured_data` in Horizon's `product-information.liquid`.[^dawn-main-product][^horizon-product-information]
- **An app**, with an app embed in the `head`[^theme-app-ext-config] or with code it has injected into the theme.[^shopify-help-apps]
- **JavaScript, after the page loads.** Google warns that Product markup generated this way can make Shopping crawls less frequent and less reliable, a problem for [price and availability](/glossary/price-and-availability/).[^google-js-sd]

The `structured_data` filter turns the product into a [Schema.org](/glossary/schema-org/) Product if it has no variants, and into a [ProductGroup](/glossary/productgroup/) if it has one or more. The example in its documentation includes the [brand](/glossary/brand-schema/), the category, the description, the image, the name, the [offer](/glossary/offer-schema/) and the URL, and **the documentation never mentions metafields at all**.[^liquid-structured-data] So don't assume your material metafield is in there: the published block is the proof, and you look at it in step 6.

If you want the data inside the markup, someone has to write it: your theme, in Liquid, or an app. In Schema.org, `material` is a Product property for the material something is made of, such as leather, wool, cotton or paper.[^schema-material] A fictitious fragment of hand-written markup, not complete markup, since it's missing the offer, the images and the rest:


```liquid
<script type="application/ld+json">
  {
    "@context": "https://schema.org",
    "@type": "Product",
    "name": {{ product.title | json }},
    "material": {{ product.metafields.custom.material.value | json }}
  }
</script>
```


The `json` filter adds the quotation marks and escapes any inside the value,[^liquid-json] so a quote in the metafield's text doesn't break the block. Two conditions before you copy it. If your theme already renders its own Product with `structured_data`, a second block like this describes the same product twice; how two nodes relate is explained in the entry on [@id in JSON-LD](/glossary/id-json-ld/). And **the material has to be visible on the page too**, with the block from step 3: Google asks you not to mark up content the reader can't see.[^google-sd-policies]

## Step 6: check it in the published HTML

Open the public URL of the product page, not the theme editor preview, which shows whichever theme you have open. If the product uses an alternate template, that URL is the one that counts (step 2). Ask for the source code, with **View page source** or `view-source:` in front of the URL, not the inspector, which shows the page after JavaScript has run. To save a copy and compare it later, one line in the terminal will do, such as `curl -s -o product-page.html https://your-store.example/products/example` (the URL is fictitious). In that code, look for:

1. **The metafield's literal value.** If it's there, it travels in the HTML the server sends. If you only see it on the page and not here, JavaScript put it there.
2. **`application/ld+json`.** Count how many blocks there are and which `@type` each one declares.
3. **Your value inside those blocks.** If you were looking for it in the markup and it isn't there, go back to step 5.
4. **The app's code**, if there is an app, before `</head>` or `</body>`.[^theme-app-ext-config]

Repeat the check every time you change theme, template or app, and whenever another tool writes to the same metafield, which is the case of [field overwrite](/glossary/field-overwrite/). To see what Google's renderer paints, the steps are in [what an AI crawler sees in your store](/guides/what-an-ai-crawler-sees/); to audit the markup itself, in [auditing a product page's structured data](/guides/audit-product-page-structured-data/). And keep the two apart: [validating the markup is not the same as verifying it gets published](/comparisons/validate-vs-verify/), which is what [publication verification](/glossary/publication-verification/) does.


**Storefront API access has nothing to do with your theme.** Turning it on doesn't make a metafield show up on the product page, and turning it off doesn't hide it from Liquid: that permission only counts for headless and custom storefronts.[^shopify-metafields-dev] What publishes the data is a template that prints it.


## What we tested

Tested on: store, theme and version, date.

| Case                                                                          | In the admin? | In the page source? | In the JSON-LD? |
| ----------------------------------------------------------------------------- | ------------- | ------------------- | --------------- |
| 1. Dynamic source in a text block of the default product template             |               |                     |                 |
| 2. The same product assigned to an alternate template without that connection |               |                     |                 |
| 3. The connection made in an unpublished copy of the theme                    |               |                     |                 |
| 4. The metafield printed with Liquid in the product section                   |               |                     |                 |
| 5. A boolean metafield: does the editor offer it as a dynamic source?         |               |                     |                 |
| 6. The theme's JSON-LD, with `structured_data`: does the material appear?     |               |                     |                 |
| 7. The fragment from step 5 added to the product section                      |               |                     |                 |
| 8. A structured data app embed, off and then on                               |               |                     |                 |

The limits, as they are: one store, one theme and one date. Themes change versions, and what one theme does says nothing about what the others do.

## Where to go next

The [publishing by platform path](/learn/publishing-by-platform/) puts the rest of the terms in order, and the guide [from widget to published page](/guides/bigcommerce-widget-to-published-page/) makes this same journey on BigCommerce.

Clione follows the road of steps 4 and 5 on Shopify: it writes its signals into metafields and renders them with two app embeds, "Clione SEO", with the JSON-LD graph in the `head`, and "Clione FAQ", with the question accordion in the `body`, which you turn on from its dashboard in one click. Its Product node carries a stable `@id` and sits next to the theme's instead of duplicating it. Then it does step 6 for you: it reads the live product page and tells you, signal by signal, what was published and what couldn't be read on the store.
