Skip to content

Dynamic sources

A dynamic source stores a typed data reference in a section or block setting instead of a fixed value. The storefront resolves that reference against the resource context of each render, so one product template can show different content for every product.

{{ product.metafields.custom.care_instructions.value }}

The reference above is persisted as the setting value. It is not evaluated by the seller UI and it is not a copy of the current product’s content.

Connect or insert a source

In the visual editor, a compatible setting can show two actions:

  • Connect dynamic source replaces the complete setting value with one source. Use it for images, URLs, colors, resource pickers, and text that should come entirely from data.
  • Insert dynamic source adds a source inside a text-like value. Static text can appear before or after it.

For example, an inserted source can persist this value:

Made by {{ product.vendor }}

On a product whose vendor is Sellerlane Studio, the setting resolves to:

Made by Sellerlane Studio

The theme still renders the resolved setting normally:

{% if section.settings.subtitle != blank %}
<p>{{ section.settings.subtitle }}</p>
{% endif %}

Supported setting types

The editor offers dynamic sources only for the following setting types.

Setting familySupported setting typesRequired source shape
Texttext, richtext, inline_richtextCompatible scalar value, or a supported list of scalar text values.
URL and colorurl, colorA URL or color value with the matching metafield type.
Mediaimage_picker, videoAn image/video field or a matching file_reference.
One resourceproduct, collection, page, article, metaobjectA matching single resource or single-reference metafield.
Resource listproduct_list, collection_list, metaobject_listA matching list-reference metafield.
Sellerlane extensionblogA direct blog context only; persisted blog_reference metafields are not supported.

metaobject and metaobject_list settings also enforce the metaobject_type declared in the setting schema. A reference to another definition type is rejected rather than coerced.

Types that render references literally

textarea, html, checkbox, number, and range do not accept dynamic sources. A value such as {{ product.title }} in one of those settings remains literal text; it is not executed.

The liquid setting type is different: it evaluates merchant-authored Liquid and is not a dynamic-source field. Do not use it merely to bypass type compatibility.

Global values in config/settings_data.json are also not connectable in the visual editor. Global settings do not have one stable page resource or closest.* scope. Put page-dependent bindings in a section or block setting.

Source and setting compatibility

The picker hides incompatible fields, and both save-time and render-time validation check the type again.

text accepts single-line text, lists of single-line text, numbers, dates, measurements, ratings, and money. inline_richtext accepts the same inline-safe shapes plus links. richtext additionally accepts multi-line text and rich-text metafields.

Examples of invalid pairings include a collection reference connected to a product setting, a list of products connected to a singular product setting, and a PDF file connected to an image_picker.

Available contexts

The picker is context-sensitive. It offers only contexts that can exist at the selected section or block instance.

ContextExample access pathWhen it is available
Template resourceproduct, collection, page, article, blogOn the matching JSON template.
Section resource settingsection.settings.featured_productWhen another section setting declares that resource type.
Block resource settingblock.settings.card_productWhen another setting on the selected block declares that resource type.
Closest resourceclosest.product or closest.metaobject["material"]In a theme block rendered inside that resource scope.
Brandshop.brandStorewide brand fields and brand metafields.
Metaobjects browsermetaobjects["material"]["cotton"]For storefront-enabled definitions and entries compatible with the setting.

The current setting is removed from its own candidate contexts, preventing a self-reference. In repeated theme blocks, the canvas selection must identify the exact rendered block instance before the editor uses its closest.* context. The editor does not silently take the first loop instance.

Which backend owns the picker state?

The state is deliberately split by responsibility.

DataAuthorityWhy
Durable draft theme files and saved setting valuesSeller backend and the pinned draft artifactThis is the editable source of truth and the publish boundary.
Metafield definitions, metaobject definitions, and metaobject entry choicesSeller backend, fetched through the buyer editor APIThese are catalog definitions, not page-render state.
Actual template, section, block, and closest.* scope for the visible previewBuyer backend renderOnly the renderer knows the exact runtime nesting and repeated block instance.
Picker UI selection and search textSeller frontendEphemeral interaction state only; it is never authorization.

Therefore, the runtime scope must come from the buyer preview, not be reconstructed by the seller backend. During a render, the buyer emits a scope graph pinned to themeId, versionId, draft revision, manifest hash, and a render revision. The seller frontend accepts it only from the expected preview window/origin and only for the active snapshot. It then sends the selected frame back to the buyer’s editor-only datasource endpoints.

The buyer endpoint requires a valid editor token, checks that the snapshot still matches the active draft, reloads the selected section/block schema from the pinned theme artifact, and verifies that the selected context is still offered. The seller backend validates the expression and catalog compatibility again when the draft is saved. No client-supplied scope graph grants storefront data access on its own.

Exact expression grammar

Dynamic sources are a restricted data-path language, not arbitrary Liquid. The editor normally writes the expression, but theme tooling should follow the same rules.

Resource fields

These first-hop fields are supported:

RootSupported direct fields
producttitle, vendor, description, url, featured_image, collections, metafields
collectiontitle, image, description, url, products, metafields
pagetitle, url, content, metafields
articletitle, url, author, content, excerpt, comments_count, image, metafields
blogtitle, url, metafields

A direct field cannot be followed by arbitrary descendants. For example, product.title.anything is invalid.

Metafields must end in .value

Use this shape:

{{ product.metafields.custom.care_instructions.value }}

Bracket notation is valid for supported persisted names:

{{ product.metafields["product.details"]["wash-care"].value }}

The object-only form below is not a valid dynamic source:

{{ product.metafields.custom.care_instructions }}

That object form remains useful in ordinary theme Liquid, but a dynamic setting needs the typed value.

Metaobjects

Select a specific entry and field:

{{ metaobjects["material"]["organic-cotton"]["display-name"].value }}

Or use the closest metaobject supplied by a theme block:

{{ closest.metaobject["material"]["display-name"].value }}

system is reserved and cannot be traversed as an authored dynamic-source field. Type handles, entry handles, namespaces, keys, and field keys are validated before lookup.

Lists of metaobjects

The picker can project a field from each entry in a list-reference metafield. The persisted expression uses structured map pairs:

{{ product.metafields.custom.materials.value | map: "display-name" | map: "value" }}

This is the only permitted filter-like syntax. Every traversed metaobject field must be followed by map: "value". Arbitrary filters such as compact, where, or join are rejected inside a dynamic source. Format the resolved setting later in ordinary theme Liquid when needed.

Common examples

Product metafield in a text setting

Schema:

{% schema %}
{
"name": "Care note",
"settings": [
{
"type": "text",
"id": "care_note",
"label": "Care note"
}
],
"presets": [{ "name": "Care note" }]
}
{% endschema %}

Saved setting value:

{{ product.metafields.custom.care_instructions.value }}

Render it defensively:

{% if section.settings.care_note != blank %}
<p>{{ section.settings.care_note }}</p>
{% endif %}

Product-reference metafield

For a product setting, connect a singular product_reference metafield:

{{ product.metafields.custom.pairs_with.value }}

Then use the resolved Drop:

{% assign paired_product = section.settings.paired_product %}
{% if paired_product %}
<a href="{{ paired_product.url }}">{{ paired_product.title }}</a>
{% endif %}

Image file metafield

For an image_picker, connect an image-valued file_reference:

{{ product.metafields.custom.size_chart.value }}
{% if section.settings.size_chart %}
{{ section.settings.size_chart | image_url: width: 1200 | image_tag }}
{% endif %}

Resource from another setting

A section can first select a fixed featured product and let another setting pull a field from it:

{{ section.settings.featured_product.title }}

The current setting cannot point to itself, and only schema-declared resource settings are exposed as contexts.

Missing data and failures

At storefront render time, dynamic sources fail soft. A deleted resource, missing metafield, blank reference, incompatible legacy value, or unavailable template resource resolves to blank/empty rather than exposing an internal error. Themes must handle that state:

{% if block.settings.badge != blank %}
<span class="badge">{{ block.settings.badge }}</span>
{% endif %}

Save-time behavior is stricter. The seller backend rejects malformed grammar, unsupported roots, unsafe keys, incompatible setting/metafield types, a wrong metaobject definition type, and limit violations. This keeps a bad binding out of a publishable draft even though runtime remains defensive for old/imported data.

Limits

ScopeLimit
One setting50 dynamic-source references
One JSON template100 references across its sections and blocks
One section group100 references across its sections and blocks
Static-section editor preflight50 references
One datasource path12 path segments
Metaobject entry picker page1–100 entries; 50 by default, with cursor pagination

Limits count references, not characters. A text value containing three {{ ... }} references consumes three slots.

Preview refresh and stale-scope errors

The editor invalidates its scope graph as soon as a preview update starts. It accepts the replacement graph only after the buyer finishes that exact render revision. This prevents a picker opened mid-refresh from binding to the previous section tree.

If the datasource endpoint returns 409 EDITOR_SCOPE_CONFLICT, the preview and editor no longer refer to the same pinned draft snapshot. Finish or retry the preview render, then reopen/reload the picker. The client should not reuse the old runtime_scope frame. Authentication failures and catalog-unavailable errors are shown separately and are retryable only when the response marks them as retryable.

Test a dynamic source

  1. Create the metafield or metaobject definition and fill a value on a real test resource.

  2. Open the matching preview page, select the exact section or repeated block instance, and connect the source.

  3. Confirm the canvas refresh shows the value, save, reload the editor, and confirm the binding remains connected.

  4. Preview a second resource with another value to prove the setting is dynamic, not copied.

  5. Clear the field and delete a referenced test resource. Confirm the section’s blank-state branch renders without a page error.

  6. Run the theme validation/release suite before publishing so the buyer parser, seller save validator, picker compatibility, persistence, and runtime render all exercise the same expression.

Troubleshooting

SymptomLikely causeFix
The braces appear on the pageThe setting type is not dynamic-source compatible.Use a supported section/block setting type.
A metafield is absent from the pickerIts owner, value type, list shape, media kind, or metaobject type is incompatible.Check the setting schema and metafield definition.
The picker shows the wrong closest.* resourceThe sidebar selection does not identify the rendered repeated-block instance, or the preview graph is stale.Select the block on the canvas and let the current preview finish.
The value is blank only on some templatesThat template has no matching primary/closest resource or the resource field is empty.Add a blank-state branch or choose a context available on every target template.
Save rejects a value that an old theme renderedRuntime soft-failure is preserving imported data, while the current save contract is stricter.Reconnect using the picker so it writes the canonical expression.
The picker asks to reloadThe draft revision/manifest changed or catalog loading failed.Reload after the current save/render completes; do not reuse a stale frame.