Dynamic Sources and Metafields: Why the Theme Looks Broken Elsewhere
The section renders. The layout is perfect. Every field inside it is empty. This is the most confusing failure in Shopify theme work, and it is almost never a CSS bug.
Why this failure is so hard to diagnose
Most theme bugs announce themselves. Liquid errors print. Missing assets 404. Broken CSS looks broken.
A missing dynamic source does none of that. Shopify resolves the binding, finds nothing, and renders an empty string. Your section markup is present and correctly styled β headings, spacing, grid, the works β with no content in it.
So the symptom is a beautifully laid out empty box. Developers reach for the inspector and start debugging CSS, because that's what an empty box usually means. The actual cause is two layers away, on the store, in a settings screen nobody opened.
Where it usually bites
- Moving a theme between stores. Definitions don't come along, so every bound field goes blank at once.
- Building on a demo store. You created the definitions on your store. The client's store has never heard of them.
- New products. Definition exists, product has no value. One product looks broken, the rest are fine β which reads like a caching bug.
- Someone deleted a definition. Deleting a metafield definition takes its values with it. Every product using it silently empties.
The four things that all have to exist
A dynamic source only renders if all four links hold. Two live in your theme and travel with it. Two live on the store and don't.
| Link | Lives in | Travels with the theme? | If missing |
|---|---|---|---|
| Section schema setting | Theme code | Yes | Setting isn't offered in the editor |
| The binding in the template | Template JSON | Yes | Falls back to the static value |
| Metafield definition | Store settings | No | Renders empty |
| Value on the record | The product / page | No | Renders empty for that record only |
Metaobjects add a fifth: the metaobject definition, plus the entries themselves. A section bound to a metaobject on a store where that definition doesn't exist renders its whole layout with every field blank β the most complete version of this failure.
{
"sections": {
"specs": {
"type": "spec-table",
"settings": {
"heading": "Specifications",
"material": "{{ product.metafields.specs.material }}",
"care": "{{ product.metafields.specs.care_instructions }}"
}
}
}
}
The theme is fine. Whether anything renders depends entirely on specs.material existing on the target store and having a value on this product.
Diagnosing it in under five minutes
Work down the chain in order. Each step rules out a layer.
- View source. Element present but empty confirms a data problem, not CSS.
- Check the definition exists. Settings › Custom data on the store. Confirm namespace and key match the binding exactly β
specs.materialis notspec.material. - Check the value on this record. Open the specific product and look at its metafields. Empty explains one broken page while others work.
- Check the type matches. A setting expecting single-line text bound to a rich-text or list metafield can render nothing or render markup as literal text.
- Try a second record. If product B renders and product A doesn't, it's values. If neither renders, it's the definition.
# All metafield bindings in templates and settings
grep -rn 'metafields\.' templates/ config/settings_data.json sections/
# Metaobject references
grep -rn 'gid://shopify/Metaobject' templates/ config/
That grep is your dependency list. Every namespace and key it returns has to exist on the target store before those templates will render anything.
Building metafield-driven themes that donβt collapse
Dynamic sources are worth using β they're how you give a merchant structured content without hard-coding copy into templates. The trick is not letting a missing value look like a broken page.
Guard for the empty case in Liquid
Don't render section furniture for content that doesn't exist. A heading above nothing is worse than no section:
{%- liquid
assign material = section.settings.material
-%}
{%- if material != blank -%}
<div class="spec-row">
<dt>Material</dt>
<dd>{{ material }}</dd>
</div>
{%- endif -%}
Apply the same idea at section level: if every field is blank, render nothing at all rather than a titled empty container. A merchant adding a product without filling in specs then gets a clean page instead of a bug report.
Setting client expectations about structured content
Metafield-driven sections shift work onto the merchant, and that needs saying out loud before launch rather than after.
The failure mode is predictable: you deliver a spec table that reads from metafields, the merchant adds forty products without filling them in, and three months later you get an email saying the theme is broken.
Two things this buys you: the merchant understands the mechanism, and you've pre-empted a support conversation that would otherwise arrive as a complaint. If you handle sign-off formally, it's worth capturing "content fields populated" as its own item rather than folding it into design approval.
How ThemeSync helps here
- You build against the store that will run the theme. A development theme on the client's real store means definitions and values are the real ones from the first commit β there's no store to migrate away from.
- Dynamic sources are detected in your templates. Templates carrying metafield bindings or metaobject references are identified, so you know which depend on store setup before moving or publishing them.
- Template mapping previews on real records. Each template opens on the actual product or page it applies to, so a blank spec table shows up during review rather than after launch.
- Screenshot runs cover every mapped template across devices. Empty sections and long-value wrapping are obvious in a capture and invisible in a diff.
- Client feedback is pinned to the element. "This box is empty" arrives attached to the exact section and viewport, so you're not reproducing it from a description.
Takeaways
- An empty-but-styled section is a data problem. View source: element present and blank means the binding resolved to nothing.
- Metafield and metaobject definitions live on the store and don't travel with a theme. Bindings do.
- All four links must hold: schema setting, binding, definition, value. Break any one and it renders empty.
- Grep for
metafields.andgid://shopify/Metaobjectto get an exact dependency list. - Create definitions and populate real values before building the sections that read them.
- Guard the empty case in Liquid so a missing value hides cleanly instead of leaving a titled void.
- Test with the longest real value in the catalogue, not a tidy placeholder.
- Tell the merchant which fields drive which sections, in writing, before launch.
Catch empty sections before your client does
ThemeSync builds on the clientβs real store, flags templates that depend on metafields and metaobjects, and captures every template across devices so blank sections surface in review.
Try ThemeSync Free →