3.9 KiB
Binding Dataclasses Deep Dive
This reference explains how to model state with NiceGUI bindable dataclasses and how to avoid common update and performance pitfalls.
Primary Sources
- NiceGUI binding docs: Binding properties
- Python dataclass docs: dataclasses module
- Data class design rationale: PEP 557
What bindable_dataclass changes
@binding.bindable_dataclass extends standard dataclasses by turning fields into bindable properties so UI bindings can propagate immediately when a field is assigned.
Baseline pattern:
from nicegui import binding, ui
@binding.bindable_dataclass
class Profile:
name: str = 'Ada'
age: int = 37
profile = Profile()
ui.input('Name').bind_value(profile, 'name')
ui.number('Age', min=0).bind_value(profile, 'age')
ui.label().bind_text_from(profile, 'name', backward=lambda n: f'User: {n}')
Propagation model and performance
NiceGUI distinguishes between two link types:
- Bindable properties: efficient, event-like propagation on assignment.
- Active links: polled in a refresh loop (default every 0.1s).
Practical implications:
- Prefer bindable dataclasses for frequently updated form state.
- Keep transform functions pure and side-effect free.
- If many active links exist, tune
binding_refresh_intervalinui.run(...)carefully.
Dataclass modeling rules that matter for binding
- Use
field(default_factory=...)for mutable defaults. - Avoid
frozen=Truefor models that should be edited from UI controls. - Use
slots=Trueonly when you have confirmed compatibility with your inheritance and extension needs. - Keep UI-editable fields explicit and typed.
Example with safe mutable defaults:
from dataclasses import field
from nicegui import binding
@binding.bindable_dataclass
class Filters:
query: str = ''
tags: list[str] = field(default_factory=list)
Nested structures and binding paths
NiceGUI supports nested key paths via tuples for nested data (for example dictionaries and nested structures).
from nicegui import ui
data = {'user': {'name': 'Ada'}}
ui.input('Name').bind_value(data, ('user', 'name'))
ui.label().bind_text_from(data, ('user', 'name'))
When using nested dataclasses, keep updates explicit and predictable at the field level.
Strictness and refactor safety
Binding can warn when attributes do not exist.
- Object attributes are checked by default.
- Dictionary keys are not checked by default.
- Use
strict=Truewhen you want missing-key warnings for dict-backed state.
from nicegui import app, ui
ui.input().bind_value(app.storage.user, 'display_name', strict=True)
Common pitfalls and safer alternatives
-
Pitfall: mutating nested mutable values in place and expecting immediate UI sync.
-
Safer alternative: assign back to the bound field after updates so change propagation is explicit.
-
Pitfall: heavy transform functions in bindings.
-
Safer alternative: keep transformations cheap and deterministic; move heavy work to event handlers.
-
Pitfall: one model shared across unrelated pages or users.
-
Safer alternative: scope model instances to page/client/user context as needed.
Version notes to remember
bindable_dataclassadded in NiceGUI 2.11.0.- Binding
strictbehavior documented as added in NiceGUI 3.0.0. - Tuple paths for nested properties documented as added in NiceGUI 3.10.0.
- Depth-first binding propagation update documented in NiceGUI 2.16.0.
Verify behavior against the NiceGUI version pinned in your project before relying on version-specific semantics.
Quick checklist
- Choose bindable dataclasses for interactive form-like state.
- Use
default_factoryfor mutable fields. - Keep transform functions pure.
- Use strict mode intentionally.
- Re-check version notes before migration work.