Progress renders a numeric completion state against a maximum. It is the component to use for upload state, ingestion state, task completion, and similar scalar progress bars.
Python Contract¶
sdk.ui.Progress(
id: str,
value: int = 0,
maximum: int = 100,
label: str = "",
show_label: bool = True,
)SDK-side behavior from the constructor:
valueis registered as mutable throughmutable_value("value"), so the capability contract includesvalue.setandvalue.append.maximum.setandlabel.setare explicitly enabled.visible.setandenabled.setare enabled through.interactive().show_label.setis not enabled by the SDK component.- The constructor serializes the
labelargument as a literal string payload. For a bound label in Python, construct the component first and then callset_property("label", ...).
value.append exists because it comes from the generic mutable-scalar helper, but for Progress it is not a meaningful operation. Use value.set.
Supported Properties¶
| Property | Type | Required | Python | YAML | Desktop | Web | Runtime updates | Notes |
|---|---|---|---|---|---|---|---|---|
id |
str |
yes | yes | yes | yes | yes | no | Stable component id |
value |
int or bound spec |
no | yes | yes | yes | yes | yes | Desktop animates value changes |
maximum |
int or bound spec |
no | yes | yes | yes | yes | yes | Web clamps percentage with max(1, maximum) |
label |
str or bound spec |
no | yes | yes | yes | yes | yes | In Python, use set_property("label", bound_spec) for bound labels |
show_label |
bool or bound spec |
no | yes | yes | yes | yes | partial | Desktop handles it as a surface re-render binding, not as a direct property mutation |
style |
str |
no | yes | yes | yes | yes | yes | Base renderer support on both clients |
show_if |
rule | no | yes | yes | yes | yes | store-driven | Generic visibility contract |
hide_if |
rule | no | yes | yes | yes | yes | store-driven | Generic visibility contract |
required_permissions |
list[str] |
no | yes | yes | yes | yes | store/auth-driven | Generic visibility contract |
capabilities |
list[str] |
no | yes | yes | yes | yes | n/a | Override only if you need a stricter or broader mutation contract |
Renderer Notes¶
Desktop:
- Renders a
QFramecontaining an optionalQLabeland aQProgressBar. valueupdates animate the bar from the current value to the target value.maximumupdates callsetRange(0, maximum).labelupdates change only the text node.show_labelis declared withSURFACEbinding strategy, so store-bound changes trigger a surface re-render instead of an in-place property patch.
Web:
- Computes
safeMaximum = Math.max(1, Number(maximum) || 100). - Computes
safeValue = clamp(value, 0, safeMaximum). - Shows the label row only when
show_labelis truthy. - The right-hand percentage label is derived client-side from
value / maximum.
Cross-client consequence:
- If you change
label, keep in mind that web also shows the computed percentage next to it. - If you need to toggle
show_label, prefer store binding rather than assuming a directshow_label.setpatch contract.
Static Example¶
progress = sdk.ui.Progress(
"upload_progress",
value=68,
maximum=100,
label="Embedding ingestion",
)- kind: Progress
id: upload_progress
value: 68
maximum: 100
label: Embedding ingestionRendered Output¶
Desktop rendering of the static example:

Web rendering of the same static example:

Store-Bound Example¶
Use bindings when progress state should be derived from page/global store values rather than patched directly.
from democrai.sdk.ui import bound
builder.set_store("/components_test/progress/value", 25, scope="page")
builder.set_store("/components_test/progress/maximum", 100, scope="page")
builder.set_store("/components_test/progress/label", "Queued", scope="page")
builder.set_store("/components_test/progress/show_label", True, scope="page")
progress = sdk.ui.Progress(
"bound_progress",
value=bound.store("/components_test/progress/value", scope="page", default=25),
maximum=bound.store("/components_test/progress/maximum", scope="page", default=100),
label="Queued",
show_label=bound.store("/components_test/progress/show_label", scope="page", default=True),
)
progress.set_property(
"label",
bound.store("/components_test/progress/label", scope="page", default="Queued"),
)- kind: Progress
id: bound_progress
value:
type: store
scope: page
path: /components_test/progress/value
default: 25
maximum:
type: store
scope: page
path: /components_test/progress/maximum
default: 100
label:
type: store
scope: page
path: /components_test/progress/label
default: Queued
show_label:
type: store
scope: page
path: /components_test/progress/show_label
default: trueAction that drives the binding:
@action("components_test_progress_set_store")
async def components_test_progress_set_store(ctx: dict, session: dict, sdk) -> dict:
del session
return sdk.effects.respond(
sdk.effects.ui_messages(
[
{
"stateUpdate": {
"scope": "page",
"values": {
"/components_test/progress/value": 64,
"/components_test/progress/maximum": 100,
"/components_test/progress/label": "Embedding ingestion",
"/components_test/progress/show_label": True,
},
}
}
]
)
)Runtime Property Update Example¶
Use direct property patches when the component instance itself is the source of truth and the mutable properties are enough.
progress = sdk.ui.Progress(
"components_test_progress_runtime",
value=10,
maximum=120,
label="Runtime patch target",
)
return sdk.effects.respond(
sdk.effects.ui_property_update("components_test_progress_runtime", "value", 72),
sdk.effects.ui_property_update("components_test_progress_runtime", "maximum", 120),
sdk.effects.ui_property_update("components_test_progress_runtime", "label", "Runtime 72/120"),
)Supported direct patches:
value.setmaximum.setlabel.setvisible.setenabled.set
Not part of the direct patch contract:
show_label.set
Practical Guidance¶
- Use direct property patches for frequent numeric progress changes.
- Use store binding when the same progress state drives more than one component or when
show_labelmust react to state. - Keep values in the same unit across clients. Both renderers assume
valueandmaximumare on the same scale. - Do not rely on values above
maximumbeing preserved visually. Web clamps tomaximum, and desktop constrains the bar range to the currentmaximum.