Section slots, overrides and exposed props
This page covers the nodes and settings that connect Sections and Templates: the Section Slot, the binding override on a placed Section, and exposed props. You need it when a Template places a Section or changes a value inside one.
The examples use the Blog project: the Blog post content type, the Post hero and Related posts Sections, and the entry AI 101.
Section Slot node
A Section Slot marks a gap in a Section that a Template fills. Studio lists the component only while you author a Section, never on a Template. The API accepts a section-slot node in a Template with no error and no warning.
{
"uid": "post_hero_byline_slot",
"type": "section-slot",
"attrs": {},
"metadata": {
"defaultSection": { "uid": "blt8888888888888888", "_content_type_uid": "compositions" },
"allowedSections": [{ "uid": "blt8888888888888888", "_content_type_uid": "compositions" }]
},
"props": {},
"slots": {},
"styles": {}
}
A Section Slot node has no slots of its own, so leave out that key. An empty array answers 422 with empty_slot.
Filling the slot from a Template
A Template fills a Section Slot through the placed Section. Any Template can fill it, connected or freeform. The placed section-composition node keys its slots on the Section Slot node's uid, and each key holds an array of nodes of any type.
{
"uid": "post_hero_placed",
"type": "section-composition",
"attrs": {},
"metadata": { "compositionUID": "blt5555555555555555" },
"props": {},
"slots": {
"post_hero_byline_slot": [
{
"uid": "byline_placed",
"type": "section-composition",
"attrs": {},
"metadata": { "compositionUID": "blt8888888888888888" },
"props": {},
"slots": {},
"styles": {}
}
]
},
"styles": {}
}
- When you drop a Section on a Template, Studio adds an empty array under
slotsfor each Section Slot. The API rejects an empty array, so remove those keys before you send. - When you save a Template, Studio removes each
slotskey that names a Section Slot the Section no longer has. The API doesn't, so a leftover key stays. - A Section can hold several Section Slots, and each has its own
uid. Studio makes a 15-character ID, and the API accepts any unique string.
The default Section and the allowed Sections
metadata.defaultSection and metadata.allowedSections take the key objects shown above. The SDK and the API read only the uid of defaultSection.
- An empty Section Slot shows its default Section on your site and in Studio's Preview mode. In Design mode it shows a drop zone and a Select section button. The default Section renders with no props and no slot fills.
- A default Section that loops back to one of its ancestors renders nothing where the loop closes.
- An empty or missing
allowedSectionsallows any Section. Only Studio enforces the list, and only when you drop a Section into the slot. A component that isn't a Section always drops in. - Studio warns: "This section isn't allowed in this slot. Choose one of the slot's allowed sections, or drop a different component."
- When the allowed list has entries, the Default section setting offers only those Sections. Changing the list to one that leaves out the default clears the default.
- The API adds each Section Slot's
defaultSectionUID tolinked_sectionsand ignoresallowedSections.
The label
The label prop has type string. Studio shows it as "Drop placeholder label". It stores the prop as a static_value binding whose key is <slot uid>-label.
{ "type": "string", "binding": { "type": "static_value", "value": "post_hero_byline_slot-label" } }
The text itself sits in the Section's static_value.text as { "key": "post_hero_byline_slot-label", "value": "Byline" }. The label shows on the empty Section Slot on the Section's own canvas, and "Drop Here" shows when it's blank. The label never renders on your site, and an empty Section Slot with no default renders nothing there.
In SEO metadata bindings, a static_value binding's value is the text itself. On a node prop, it's always a key.
Repeater context
A Section Slot passes the Section's Repeater context to its fill, so the fill reads each item with a repeater binding. A Template Repeater around the placed Section also reaches the fill, because the Section keeps the enclosing Repeater map.
In Studio, a placed Section starts in Design mode when its Section has a Section Slot, and in Preview mode when it has none. The placed Section's metadata.mode sets the mode of its Repeaters, Condition Blocks and Section Slots in Studio. Your site ignores it.
Binding override on a placed section
A Template places a Section with a section-composition node. Its metadata.compositionUID is the Section's entry UID, not its composable_uid.
{
"uid": "post_hero_placed",
"type": "section-composition",
"attrs": {},
"metadata": { "compositionUID": "blt5555555555555555" },
"props": {},
"slots": {},
"styles": {}
}
The create call answers with the new Section's entry uid, the value to put in compositionUID. A compositionUID that isn't a Section's entry UID shows the error "Component with type '
linked_sections
You don't send linked_sections. The API ignores a value you send and fills it from ui on every write that carries ui. It holds one { "uid", "_content_type_uid" } row for each compositionUID in ui, fills inside slots included. A PUT without ui leaves the stored value as it was.
A Section gets linked_sections rows too, for the Sections it places and for each Section Slot default. Studio finds the Templates that use a Section through these rows. The API warns "ensure linked_sections references every placed section" on every write that places a Section, though it fills the field. You can ignore that warning.
Pointing the Section at other data
A placed Section reads its data from metadata.sectionBindingOverride when it's set. Otherwise it reads the Section's linked_schemas.
{
"uid": "post_hero_placed",
"type": "section-composition",
"attrs": {},
"metadata": {
"compositionUID": "blt5555555555555555",
"sectionBindingOverride": {
"selectedField": "related_posts",
"uidRemapping": {}
}
},
"props": {},
"slots": {},
"styles": {}
}
Field of sectionBindingOverride | What it does |
|---|---|
selectedField | The field the Section reads, starting at the data where the placed Section sits. In linked_schemas, selected_field starts at the entry. Inside a Template Repeater it narrows each item, and if it finds nothing in the item, the Section reads the whole item. |
uidRemapping | { "<Section field key>": "<Template field key>" }. The SDK applies each pair at every depth. An empty or missing map leaves every key as it is. |
rootBinding | A template, repeater, contentstack or contentstack_queries binding that replaces the Section's own data. null renders the Section with no data. A missing rootBinding keeps the declared field. When it's present, the SDK ignores selectedField. |
matchedSchemaCt | The linked_schemas entry that applies. A freeform Template needs it because it has no content type. |
- Without
rootBinding, the SDK readssectionBindingOverride.selectedField, thenmetadata.selectedField, thenlinked_schemas. - From
linked_schemas, the SDK takes theselected_fieldof the entry for the Template's content type, else the first entry. It doesn't run the matching rules, so an API-placed Section reads its declaredselected_fieldunless the node has an override. - Choosing a value in the Section Data row writes
rootBinding,uidRemappingand sometimesmatchedSchemaCt. It also sets flatmetadata.selectedFieldandmetadata.uidRemapping, for older SDKs. - On save, Studio writes
{ "selectedField", "uidRemapping" }only for a placed Section whose field or remapping differs from itslinked_schemas. - A pinned entry or query in
rootBindingstays the same in every iteration of an enclosing Repeater. - Each
linked_schemasentry has up to four fields:content_type_uid,selected_field,display_nameandcardinality. Leaving outselected_fieldbinds the whole entry. - The API checks neither
sectionBindingOverridenor anylinked_schemasvalue against your content types.
Exposed section props
A Section author can let a Template change one prop inside the Section. The exposed props are an array in sectionExposedProps on the Section root node's metadata. It isn't a node you drop.
This entry exposes the Heading Level of Post hero's Header as "Title size".
{
"uid": "post_hero_root",
"type": "page",
"attrs": {},
"metadata": {
"sectionExposedProps": [
{
"uid": "title_size",
"nodeUid": "hero_title",
"propKey": "Tag",
"displayName": "Title size",
"propType": "choice",
"bindingAtExposeTime": { "type": "choice", "value": ["h1"] }
}
]
},
"props": { "children": { "type": "slot", "slot": "root_slot" } },
"slots": {
"root_slot": [
{
"uid": "hero_title",
"type": "header",
"attrs": {},
"metadata": {},
"props": {
"text": {
"type": "plaintext",
"binding": { "type": "template", "value": { "path": { "title": {} } } }
},
"Tag": { "type": "choice", "value": ["h1"] }
},
"slots": {},
"styles": {}
}
]
},
"styles": {}
}
| Field | What it holds |
|---|---|
nodeUid | The uid of the node inside the Section that owns the prop. |
propKey | The prop's name on its component. |
uid | The key the placed Section stores its override under. Studio builds it from the trimmed displayName, lowercased, with each run of spaces turned into _. |
displayName | What a Template author sees under "Exposed As". |
propType | The prop's type. It decides the control. A string or plaintext prop gets a text input. A boolean, number or datestring prop gets its own input. A choice prop gets a select, an imageurl prop an image input and an href prop a link input. |
bindingAtExposeTime | The binding the prop had when it was exposed. |
Studio writes sectionExposedProps when you click Save on a Section and confirm the Expose Props dialog. A save Studio starts on its own skips the dialog and keeps the stored exposed props. The dialog lists every prop of every node in the Section except slot props, Section Slots and placed Sections. It refuses an empty name with "Display name cannot be empty" and a repeated one with "This display name is already used".
- The API doesn't check
sectionExposedProps. It accepts two entries with the sameuid, and an entry with nopropTypeand nodisplayName. - Send
propTypeon every entry, because the SDK registers the Section's props from eachuidandpropType. WhenpropTypeordisplayNameis missing, Studio uses the component's own prop type and a name made frompropKey. - Studio copies a
choiceprop's options from the original component, so the Section doesn't store them.
Setting an override on the Template
A placed Section stores each override on its own props, keyed by the exposed prop's uid, as { "type": <propType>, "binding": … }.
{
"uid": "post_hero_placed",
"type": "section-composition",
"attrs": {},
"metadata": { "compositionUID": "blt5555555555555555" },
"props": {
"title_size": {
"type": "choice",
"binding": { "type": "static_value", "value": "post_hero_placed-title_size" }
}
},
"slots": {},
"styles": {}
}
The override needs a binding. A plain value on the placed node's prop doesn't override the Section. A typed value gets its own copy on each placed Section, stored under the key <placed node uid>-<exposed prop uid>.
- A placed Section prop with no binding falls back to the Section's own binding. A binding marked
"autoSeeded": truefalls back too. - On drop, Studio seeds an exposed prop with an
autoSeededcopy of itsbindingAtExposeTime, unless that binding is astatic_value. For atemplatebinding, the seed puts the Section'sselected_fieldin front of the path. - A
bindingAtExposeTimeof typerepeaterstays with the Section until you rebind the prop on the Template to a source the Template can read. - At render the SDK builds
section_exposed_overridesas{ "<nodeUid>": { "<propKey>": <value> } }from the placed node's resolved props. It also holds a$<propKey>entry with the live preview tag of the Template field. - A prop you rebound on the Template and then cleared still overrides, so the Section's own binding doesn't come back.
- An override for an unbound or untouched prop isn't in
section_exposed_overrides.
For the editor steps, see Build sections.
For whole trees you can copy, see Section and template JSON examples.