Files
prompts/docs/skills/nicegui/references/binding-dataclasses.md
T
2026-06-23 19:46:35 -05:00

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

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_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:

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=True when 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_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.