nicegui-builder is a Python library for building NiceGUI interfaces from declarative layouts and schema-aware plugins.
Warning
This is an early pre-release published to invite discovery and feedback. The project is not presented as production-ready or fully stabilized yet. Expect API changes, rough edges, and evolving behavior while the library is still taking shape.
It currently provides three main entry points:
builder(layout)for declarative NiceGUI renderingform(source, flavor="")for plugin-driven formstable(source, variant="std")for plugin-driven tabular views
Base install:
pip install nicegui-builderOptional pandas support:
pip install "nicegui-builder[pandas]"CLI entry point:
nicegui-builder --helpTop-level imports from nicegui_builder are the supported stable public API.
See docs/public-api.md for the current stable surface.
Use builder(...) when you already have a declarative layout.
import yaml
from pathlib import Path
from nicegui import ui
from nicegui_builder import builder
layout = yaml.safe_load(
Path("src/nicegui_builder/examples/demo_basic_builder.yml").read_text(encoding="utf-8")
)
builder(layout)
ui.run()Use form(...) when you want a plugin to inspect a supported source and render a form.
from pydantic import BaseModel
from nicegui import ui
from nicegui_builder import form
class Contact(BaseModel):
firstname: str
lastname: str
email: str
handle = form(Contact)
ui.run()If a matching YAML layout is not found, the plugin can fall back to an automatic layout.
Built-in automatic pydantic flavors:
std: responsive editable gridcompact: simple stacked editable layoutdetail: read-only detail viewfilters: search-oriented filter formactionable: editable layout with status, error, and action areas
Examples:
form(Contact, flavor="compact")
form(Contact, flavor="detail")
form(Contact, flavor="filters")
handle = form(Contact, flavor="actionable")For datetime fields, the built-in pydantic plugin uses a split date_input + time_input widget by default.
That split stays grouped as one logical field, but the layout can choose its wrapper container.
Example:
- field__starts_at:
container: grid
params:
columns: 2
classes: col-span-12 gap-2That lets you keep the date/time pair together while placing it inside a row, column, grid, or another declarative container.
Use table(...) when you want a plugin to inspect a collection-like source and render a table.
import pandas as pd
from nicegui import ui
from nicegui_builder import table
df = pd.DataFrame(
[
{"name": "Ada", "country": "UK"},
{"name": "Grace", "country": "US"},
]
)
handle = table(df)
ui.run()For the pandas plugin, a richer filtered table variant is also available:
handle = table(df, variant="filters")form(...) returns a FormHandle.
Read values:
handle.get_values()Rebuild a model:
handle.to_model()
handle.submit(lambda model: print(model), as_model=True)Validate:
result = handle.validate(as_model=True)
print(result.valid)
print(result.errors_by_field())
print(result.error_messages())Reset:
handle.reset_to_source()
handle.reset_to_defaults()
handle.reset_to_empty()Track changes:
handle.changed_fields()
handle.is_dirty()Live helpers:
handle.live_dirty_badge()
handle.live_reset_button()
handle.live_submit_button(
"Save",
lambda model: print(model),
as_model=True,
strategy="dirty_and_valid",
)
handle.live_error_panel(as_model=True, strategy="invalid")Validation helpers:
handle.apply_errors(as_model=True)
handle.clear_errors()
handle.validate_field("email", as_model=True)
handle.field_error_message("email", as_model=True)
handle.apply_field_errors("email", as_model=True)
handle.live_validation(as_model=True, mode="change")Action helpers:
handle.submit_button("Save", lambda model: print(model), as_model=True)
handle.reset_button()
handle.create_button(lambda model: save_new(model))
handle.update_button(lambda model: save_existing(model))
handle.delete_button(lambda instance: delete_instance(instance))
handle.crud_bar(
on_create=lambda model: save_new(model),
on_update=lambda model: save_existing(model),
on_delete=lambda instance: delete_instance(instance),
)
handle.action_bar(
"Save",
lambda model: print(model),
as_model=True,
live_changed_badge=True,
live_reset_button=True,
live_submit_button=True,
submit_strategy="dirty_and_valid",
)table(...) returns a TableHandle.
Inspect the table:
handle.spec
handle.component
handle.get_rows()Sort, paginate, select, and export:
handle.sort_rows("name")
handle.set_pagination(rows_per_page=25, sort_by="score", descending=True)
handle.set_selected_rows([{"name": "Ada"}])
csv_text = handle.export_csv()Filter:
handle.set_filter("name", "ada", op="contains")
handle.set_filter("score", [10, 20], op="between")
handle.apply_filters()
handle.clear_filters().apply_filters()CRUD-style actions:
handle.crud_bar(
on_create=lambda table_handle: open_create_dialog(),
on_delete_selected=lambda rows: delete_rows(rows),
on_export=lambda csv_text: print(csv_text),
)The project also exposes a small CLI for quick experimentation.
List bundled examples:
nicegui-builder examples listRun a bundled example:
nicegui-builder examples run 01_basic_builder --port 8080
nicegui-builder examples run 03_pydantic_form_basic --port 8080Render a YAML layout directly:
nicegui-builder layout run path/to/layout.yml --port 8080Render a form from a Python object path:
nicegui-builder form run my_module:MyModel --flavor actionable --port 8080This is especially useful to:
- launch bundled examples quickly
- test a declarative layout without writing a dedicated script
- try a form source interactively while tuning layouts and flavors
Use Ctrl+C as the normal cross-platform way to stop a running CLI session.
The pydantic plugin resolves field__... nodes using:
- field type
- field constraints and metadata
- plugin-local widget mapping in
src/nicegui_builder/plugins/pydantic/pydantic-nicegui.yml
Example:
- card.tight:
classes: p-3 gap-3
children:
- label:
params:
text: Contact info form
- grid:
params:
columns: 12
classes: w-full
children:
- field__firstname:
classes: col-span-6
- field__lastname:
classes: col-span-6
- field__email:
methods: email
classes: w-full
- field__starts_at:
container: grid
params:
columns: 2
classes: col-span-12 gap-2docs/public-api.md: stable public APIdocs/plugin.md: how to build a new plugindocs/architecture.md: architecture and class diagramdocs/publishing.md: TestPyPI and PyPI publishing guidedocs/product-roadmap.md: roadmap history