Bind data to a node
This page shows how a node's props get their values: the six value sources, and how to write a path to a field. You need it when a prop shows a field, a fixed value, a pinned entry, a query or data your app passes in.
The examples use the Blog project: the Blog post content type, the Post hero and Related posts Sections, and the entry AI 101.
Value sources
A binding's type says where a prop's value comes from. Studio has six.
binding.type | binding.value | Reads |
|---|---|---|
template | { "path": … } | A field of the connected entry |
static_value | A string key | A fixed value stored in the composition's static_value |
contentstack | { "uid", "_content_type_uid", "path" } | A field of one pinned entry |
contentstack_queries | { "queryUID", "path" } | The results of a pinned query |
repeater | { "repeaterUID", "path" } | A field of the current item in a Repeater |
component_props | A string | A key in the data your app passes to the component |
The API doesn't validate a binding's type or value, so a wrong one saves without an error. For how to bind in the editor, see Data sources.
Static values
A static_value binding's value is a key, not the text you want to show. Studio names each key <nodeUID>-<propName>, and the SDK accepts any string.
{ "type": "static_value", "value": "hero_title-text" }
The text lives in the composition's static_value field, an object of typed buckets such as text, plaintext, number, boolean and choice. Each bucket is a list of { key, value } items.
{
"text": [{ "key": "hero_title-text", "value": "The team blog" }],
"choice": [{ "key": "hero_title-Tag", "value": ["h1"] }]
}
- The SDK merges every bucket into one map, so the bucket name doesn't change which key matches.
- In the
objectandarraybuckets the value is a JSON string, and the SDK parses it. - A
choicevalue is a list of strings. A single select prop receives the first item. - A new composition starts with an empty
static_valueobject. - When a key has no match, the prop shows its own
value, then the registereddefaultValue, then an empty value for its type. The SDK logs no error and shows no warning. - When you save in Studio, it deletes
static_valueitems whose key doesn't start with a node UID from the tree. - Inside a Section, the Section's own keys override the Template's keys of the same name.
Pinned entries and queries
A contentstack or contentstack_queries binding reads from data_sources. The API takes data_sources as an array and stores it as a JSON string.
[
{
"uid": "contentstack",
"data": [{ "uid": "blt9f8e7d6c5b4a3210", "_content_type_uid": "author" }]
},
{
"uid": "contentstack_queries",
"data": [
{
"uid": "latest-posts",
"name": "Latest posts",
"spec": {
"contentType": "blog_post",
"sourceType": "content_type",
"query": {},
"params": { "limit": 3, "desc": "created_at" }
}
}
]
}
]
- The SDK reads a pinned entry from
dataSources.contentstackunder the key<content type uid>:<entry uid>. - A query
spechas four fields:contentType,sourceType,queryandparams.sourceTypedefaults tocontent_type, and Studio writes only that value. - In
params,limit,skip,ascanddescmap to the Delivery API.descandasctake a field UID. - The SDK sends
params.includeas reference includes, but Studio removesinclude,onlyandexceptfrom the filters you type. - Studio sets a new query's
uidto its name in lowercase with spaces changed to hyphens, and keeps it when you rename the query. - A
contentstack_queriesbinding reads an object withentriesandcount, not a bare list. A Repeater over a query reads its items frompath: {}orpath: { "entries": {} }. - A query that isn't in
data_sourcesnever runs, so a binding to itsqueryUIDshows the prop's default. The SDK skips a query whosespechas nocontentType, with no error. - A failed query request returns
{ entries: [], count: 0 }, so the list is empty and no error shows. - A pinned entry the Delivery API doesn't return leaves its bindings on the prop's default.
- SDK 2.0.0 runs Pinned Queries.
Component data
A component_props binding reads the data prop of <StudioComponent />, not a prop named componentProps. Its value can be a dotted path, such as newsletter.subscribers.
- The Component Data you type in Studio is stored as the
data_sourcesitem with theuidexternal_data. - Without a
dataprop,component_propsreadsexternal_data. When you passdata, the SDK ignoresexternal_datafor the Template, even ifdatais an empty object. - With neither, the binding shows the prop's default.
- Inside a Section, the Section's own
external_datafills the keys that thedatayou pass leaves out.
Which sources each kind of composition has
| Source | Connected Template | Freeform Template | Section |
|---|---|---|---|
template | The entry you pass as templateEntryUid, or else the entry the URL pattern resolves to | None. The binding shows the prop's default | The linked_schemas entry that matches the Template's content type, or the first entry |
contentstack, contentstack_queries | Its own | Its own | Declared on the Section, and travels with it into every Template that embeds it |
static_value, component_props | Yes | Yes | Yes |
A Section's selected_field narrows its template scope to that field. A Section's own scoped data holds only the pinned entries and queries its data_sources declares. On a freeform Template, a placed Section can read a pinned entry or a query instead of a Template entry.
Bind one field to a prop
A bindable prop is stored as { "type": "<prop value type>", "binding": { "type", "value" } }. For a field of the connected entry, the binding is a template source with a path.
{
"type": "plaintext",
"binding": { "type": "template", "value": { "path": { "title": {} } } }
}
In binding.value.path, each key is a field name. Its value is a nested path object, or {} at the end. The SDK reads only the first key at each level, so a second sibling key is ignored.
| You want | path |
|---|---|
The Blog post title | { "title": {} } |
The canonical field in the SEO group | { "seo": { "canonical": {} } } |
The Author card global field's name | { "author_card": { "name": {} } } |
The Author's title, through the reference field author | { "author": { "title": {} } } |
| The whole bound scope | {} |
path: {}reads the whole bound scope: the entry on a Template, the narrowed field in a Section, and the current item in a Repeater.- One key with dots, such as
{ "seo.canonical": {} }, resolves the same as nested keys. Studio writes a Template or pinned entry path as one dotted key, and starts a new nested key only after a reference field. It writes a query path and a Repeater item path as nested keys, one for each segment. - Studio writes an empty string key when you pick a reference field, as in
{ "author": { "": {} } }. It reads the same as{ "author": {} }. - A numeric key reads that array position, as in
{ "entries": { "0": { "title": {} } } }. When Studio scopes a Section's data, a path that lands on an array without an index reads element 0.
What a prop shows for each field type
-
A
stringorplaintextprop accepts text, number and boolean values. Aplaintextprop bound to a rich text field gets the raw HTML string, with no conversion. -
A
stringprop bound to a group with one key shows that key's value. Bound to a group with several keys, it shows this message.This content may not display correctly in this view. Please contact your development team to update the component so it can support this type of content.
-
To bind a whole group, global field or block, use an
objectprop.objectandarrayprops skip the one-key unwrap. -
When an
arrayprop gets a value that isn't a list, the SDK passes it through and warns in the console. When a scalar prop gets a value of the wrong type, the SDK swaps in the prop's fallback without a warning.
Next, see Repeaters and condition blocks to repeat a node for each item or show it only when a condition holds.