# 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](https://www.nicegui.io/documentation/section_binding_properties) - Python dataclass docs: [dataclasses module](https://docs.python.org/3/library/dataclasses.html) - Data class design rationale: [PEP 557](https://peps.python.org/pep-0557/) ## 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: ```python 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_interval` in `ui.run(...)` carefully. ## Dataclass modeling rules that matter for binding - Use `field(default_factory=...)` for mutable defaults. - Avoid `frozen=True` for models that should be edited from UI controls. - Use `slots=True` only when you have confirmed compatibility with your inheritance and extension needs. - Keep UI-editable fields explicit and typed. Example with safe mutable defaults: ```python 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). ```python 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=True` when you want missing-key warnings for dict-backed state. ```python 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_dataclass` added in NiceGUI 2.11.0. - Binding `strict` behavior 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_factory` for mutable fields. - Keep transform functions pure. - Use strict mode intentionally. - Re-check version notes before migration work.