<- Back to Components Index

This page describes the shared binding and update contract used by component pages.

Python Bindings

Use bound.store(...) for page or global store values.

from democrai.sdk.ui import bound

title = bound.store("/draft/title", scope="page", default="")
theme = bound.store("/theme", scope="global", default="dark")
current_path = bound.store("/current_path", scope="auto", default="/")

Use bound.data(...) for the current surface data model.

rows = bound.data("/components_test/table_model/rows", default=[])

Use bound.literal(...) when a value must stay literal even if it looks like binding syntax.

label = bound.literal("@state/page/title")

Use bound.action(...) for client-resolved action values.

items = bound.action(
    "components.load_items",
    args={"limit": 20},
    default=[],
    cache_scope="page",
)

YAML Bindings

Compact store bindings:

- kind: Text
  id: draft_title
  text: "@state/page/draft/title"

Store scopes:

YAML Python equivalent
@state/page/path bound.store("path", scope="page")
@state/global/path bound.store("path", scope="global")
@state/path bound.store("path", scope="auto")

Data-model binding:

- kind: Breadcrumb
  id: page_breadcrumb
  segments: "@data/components_test/breadcrumb_model/segments"

Explicit store binding:

- kind: Text
  id: draft_title
  text:
    type: store
    scope: page
    path: /draft/title
    default: ""

Other compact forms:

YAML Meaning
@action/name Action-bound value.
@literal/value Literal value.
@t/module.key Translation key resolved while parsing YAML.
@tag/name Runtime tag constant when supported.

Store Updates

Actions update store values with a stateUpdate message.

return sdk.effects.respond(
    sdk.effects.ui_messages([
        {
            "stateUpdate": {
                "scope": "page",
                "values": {
                    "/components_test/breadcrumb/segments": segments,
                },
            }
        }
    ])
)

Use scope: "global" only when the value is intentionally shared across pages or modules.

Data Model Updates

Actions update the surface data model with build_data_model_update_payload(...).

surface_id = ctx["_surface_id"]

return sdk.effects.respond(
    sdk.effects.ui_messages([
        sdk.ui.Builder.build_data_model_update_payload(
            surface_id=surface_id,
            data={
                "components_test": {
                    "breadcrumb_model": {
                        "segments": segments,
                    }
                }
            },
        )
    ])
)

Always use the action surface id when updating data for the current surface.

Property Updates

Direct component updates use ui_property_update(...).

surface_id = ctx["_surface_id"]

return sdk.effects.respond(
    sdk.effects.ui_property_update(
        "page_breadcrumb",
        "segments",
        segments,
        surface_id=surface_id,
    )
)

Declare the matching capability on the component.

breadcrumb.allow("segments.set")
- kind: Breadcrumb
  id: page_breadcrumb
  capabilities: [segments.set]

Collection Updates

Collection helpers are property updates with collection actions.

return sdk.effects.respond(
    sdk.effects.ui_collection_append(
        "content_stack",
        "children",
        sdk.ui.Text("item_2", "Item 2").to_dict(),
        surface_id=ctx["_surface_id"],
    ),
    sdk.effects.ui_collection_remove(
        "content_stack",
        "children",
        {"id": "item_2"},
        surface_id=ctx["_surface_id"],
    ),
)

Declare collection capabilities on the target component.

column.allow("children.append", "children.remove")
- kind: Column
  id: content_stack
  capabilities: [children.append, children.remove]

Visibility Rules

show_if and hide_if use the same binding payloads.

component.set_show_if({
    "conditions": [
        {
            "left": bound.store("/can_view", scope="page", default=True),
            "op": "==",
            "right": True,
        }
    ]
})
show_if:
  conditions:
    - left: {type: store, scope: page, path: /can_view, default: true}
      op: "=="
      right: true

Use required_permissions for RBAC visibility.

component.set_required_permissions(["components.content.view"])
required_permissions: [components.content.view]

Practical Rules

  • Bind to store for UI state that can be shared or changed by actions.
  • Bind to the data model for data that belongs to a specific surface.
  • Declare capabilities for every direct property or collection mutation.
  • Use ctx["_surface_id"] for property updates and data model updates from actions.
  • Do not document a binding or update pattern for a component until the Python and YAML examples are reproducible in the components module test page.