Studio Docs

Linked-Schema Matching Rules

When a section is dropped onto a template, Studio looks for fields on the template’s connected content type that structurally match the section’s linked schema. This page is the reference for the matching algorithm: every rule, every edge case.

For the concept-level explanation, see Linked schema and Auto-binding.

Scope. These are the drop-time rules: what Studio auto-binds a section to when it lands on a template. The per-placement Section Data control accepts a wider set of targets on the same foundations — pinned entries, query results, whole documents, and shapes the section is merely contained by. Those rules live in Section Data reference.

Do It With a Skill

understand-linked-schemas explains why declaring the shape once makes a Section auto-bind. When matching does not behave as this reference says it should, troubleshoot-data-binding is the diagnostic.

curl -fsSL https://studio-documentation.contentstackapps.com/install.sh | sh

The Match Question Studio Asks

For each candidate field on the template’s content type schema, Studio asks:

Could a section configured for <section's linked field> bind to <this template field> and have its internal bindings resolve correctly?

The answer is yes when the structure matches. Field names don’t have to be the same, and Studio builds a positional remap by data type when names differ.

Per-Data-Type Matching Rules

data_type === "group" or "global_field" (and multiple flag agrees)

A match requires:

  1. Same number of immediate children
  2. The sorted set of children’s data_types is identical

The child names can differ; Studio builds a positional remap (e.g. section’s heading ↔ page’s title) when names don’t match.

Containment (Section Data only). Drop-time auto-binding needs the two shapes to be equal. The Section Data picker runs a second pass that also accepts section ⊆ target: every child the section declares is present on the target, paired order-preserving by data type, and extra fields on the target are ignored. A section shaped like {text, text, file} can therefore be rooted at a group that also carries a link and a number. See Section Data reference § Matching.

Four matching examples for group / global_field fields, identical multisets match (order-invariant). Different child counts or types do not

data_type === "blocks" (modular blocks)

Looser rule: a match is declared if the section’s blocks union and the template field’s blocks union share at least one block UID.

Three rows showing the modular blocks intersection rule — any shared block UID declares a match; disjoint unions do not

Why looser: sections typically wrap their Repeater in a ConditionBlock that narrows to one specific block UID, so they care that the targeted block exists on both sides, not whether the unions are equal. Strict set equality would refuse the common recursive-shape pattern where the template field’s nested modular_blocks allows only the inner block type while the section’s modular_blocks allows several at root.

data_type === "reference"

Strict rule: a match requires the section’s reference_to union and the template field’s reference_to union to be equal as sorted sets. Every CT in one must be present in the other, and vice versa. Order in the schema doesn’t matter (the comparison sorts both sides first), but the membership must be identical.

Four rows showing strict sorted-set equality for reference fields — order-invariant equality matches; subsets, supersets, or disjoint unions do not

Why strict (unlike blocks): references have no ConditionBlock narrowing mechanism; at runtime a reference field can resolve to any CT in its union, and the section’s internal bindings assume the exact union it was authored against. Accepting a subset would let bindings reference CTs the template’s field can’t actually hold; accepting a superset would let the template surface CTs the section doesn’t know how to render. Sorted-set equality is the only safe rule.

If the unions differ even by one CT, the section won’t auto-bind. Fix it by aligning the reference_to lists on both content types, or by adding a second linked-schema entry to the section that targets the alternate shape.

Scalar types (text, number, file, etc.)

Same data_type plus same multiple flag. No child structure to check.

Three rows showing scalar matching — same data_type and same multiple flag matches; differences in either do not

The multiple Flag

Across all types, the section’s multiple flag must match the template field’s. A single-value field never matches a multi-value field, even if the data types align.

Three Drop Outcomes

After applying the rules, Studio computes the match count and resolves to one of three outcomes:

Matches foundOutcome
Exactly 1Auto-picked. The section binds to that field without user input. The right-panel Properties → Section Data row shows what it is reading from (the control is the same on every section drop).
More than 1Studio binds to one (the first in schema order). The Section Data picker offers the other matches; the author can switch. Per-instance: dropping the same section twice on the same template lets each instance read from a different field.
ZeroSection drops unbound and renders component defaults. The author can pick a compatible target from the Section Data picker — including a pinned entry or a query, which auto-binding never offers — or move the section to a different scope.

Section placement selected on a template canvas — right-panel Properties shows the Section Data row the section is reading from. The same control appears on every section drop; the picker just offers more targets when several match.

Scope-Aware Matching

Studio doesn’t only check the template’s top-level fields. It walks into the scope at the drop location:

Drop locationScope walked
Root of the templateTop-level fields of the template’s connected content type
Inside a RepeaterSchema of the Repeater’s iteration item
Inside a Modular Block (under ConditionBlock)Fields of that specific block
Nested combinationsInnermost scope; scopes chain

The matcher runs against the resolved scope’s schema, not the template’s top-level schema. So a section anchored on gf_card can match an items[] repeater of gf_card (root scope), match a card field inside a CTA block (block scope), or scope-root-match the iteration item itself when iterating over a list of gf_card.

Scope-root match (special case)

When the surrounding scope itself mirrors the section’s shape (e.g. a Repeater iterating a list of gf_featured_card, or a Modular Block whose schema is the Global Field), the section binds directly to the scope root. No wrapper field needed. The section “becomes” the iteration item.

This is what makes the section-inside-Repeater-of-its-own-shape pattern work cleanly. See Card grid with slots recipe.

The Positional UID Remap

When a group or global_field match is declared and the child names differ, Studio builds a remap by position within data-type:

Section schema heading/body/image remapped by position to Page schema title/desc/hero via three indigo arrows

Bindings inside the section that reference template.heading now resolve through page.title at render time. The author doesn’t see this; the section’s UI says template.heading; Studio handles the translation.

The remap is shallow (one level deep)

The remap only translates top-level child names. Nested groups must use the same name on both sides.

Three schema cards demonstrating the shallow nested-group remap — Page A matches because the nested group keeps the name 'inner'; Page B fails because it was renamed to 'content'

For nested shapes, prefer Global Fields. Embed the same Global Field everywhere; the structure is literally identical and the remap question never comes up.

What the User Override Looks Like

If Studio’s auto-pick is wrong (or if you want a non-default match), the section’s right-panel Settings shows a Linked Field dropdown when multiple matches exist. Picking a different field:

  1. Updates the section’s binding for this drop instance
  2. Builds a new positional remap against the chosen field
  3. Stores the override on the template (not on the section composition)

The override is per-instance; different drops of the same section can target different fields.

What’s NOT Supported

Documented to avoid surprises:

  • Renaming a nested group’s children with the remap. Only top-level remap is supported.
  • Reordering a section’s binding to a different field of the same shape via prop. Use the right-panel Linked Field dropdown instead.
  • Cross-CT match without a linked schema entry. A section with linked_schemas: [blog_post] will not match product‘s fields. Add product to the section’s linked schemas to enable matching there.

Algorithm Summary (for the Curious)

def match(section_field, page_scope):
    matches = []
    for page_field in page_scope:
        if not same_data_type(section_field, page_field): continue
        if not same_multiple_flag(section_field, page_field): continue
        if page_field.data_type == "blocks":
            if section_field.blocks ∩ page_field.blocks: matches.append(page_field)
        elif page_field.data_type == "reference":
            if sorted(section_field.refs) == sorted(page_field.refs): matches.append(page_field)
        elif page_field.data_type in ("group", "global_field"):
            section_children = sorted(child.data_type for child in section_field.schema)
            page_children    = sorted(child.data_type for child in page_field.schema)
            if section_children == page_children: matches.append(page_field)
        else:
            matches.append(page_field)
    return matches

This is illustrative; actual implementation handles edge cases like missing schemas and scope-root variations.

See Also