Cookbook
Short, repeatable recipes for everyday Wijjit tasks. Each entry links to a working example so you can copy-paste with confidence.
Form validation with inline errors
Source: examples/advanced/login_form.py
Bind text inputs to state keys (
id="username"/"password").Use
@app.on_action("login")to validate fields; populatestate.form_errorswith error messages.Render errors next to inputs using template conditionals:
{% if state.form_errors.username %} {% text class="error" %}{{ state.form_errors.username }}{% endtext %} {% endif %}
For a transient toast instead of inline text, call
app.notify(state.form_errors["username"], severity="error")from your action handler.Clear errors when the form succeeds or users edit the fields.
Persisting data between sessions
Source: tutorial todo app (see Tutorial: Persistent Todo App)
Load data from disk when constructing
Wijjit; store anext_idcounter in state.Define helper
persist()that writesstate["todos"]back to JSON.Call
persist()inside@app.on_actionhandlers and/orstate.watch("todos", ...)so every change is saved.Keep file paths under
Path.home()or configurable via environment variables.
Background tasks & progress indicators
Source: examples/basic/async_demo.py and examples/advanced/download_simulator.py
Mark the handler
async def(Wijjit awaits coroutines).Update
state["progress"]inside the coroutine and callawait asyncio.sleep(...)between iterations.Render
progressvia{% progressbar value=state.progress %}{% endprogressbar %}.For CPU-bound work, opt into the built-in executor with
app.config["RUN_SYNC_IN_EXECUTOR"] = True(and optionallyapp.config["EXECUTOR_MAX_WORKERS"] = N) so blocking sync handlers run off the event loop and keep the UI responsive.
Modal confirmations before destructive actions
Source: examples/widgets/modal_with_button_demo.py
Define a template snippet with
{% confirmdialog action_ok="confirm_delete" action_cancel="cancel_delete" %}.Show the dialog by updating a boolean in state or by returning overlay metadata from the view.
Handle the confirm/cancel actions with
@app.on_action; callapp.overlay_manager.pop_layer(LayerType.MODAL)when done.Use
trap_focus=Trueanddimmed_background=Truefor critical workflows.
Data tables with selection
Source: examples/widgets/table_demo.py
Prepare rows as list of dicts; include
selected_rowin state.Pass
rowsandcolumnsto the table tag; useaction="select_row_{{ row.id }}"on buttons/checkboxes inside cells.Handle selection in a generic
EventType.ACTIONhandler by parsing the action id (select_row_prefix).Reflect selection elsewhere (details pane, status bar, modal).
Notifications & status feedback
Source: examples/widgets/notification_demo.py and examples/widgets/statusbar_demo.py
For transient toasts, call
app.notify("Saved!", severity="success"). The notification auto-dismisses afterdurationseconds (passduration=Noneto make it persistent).For persistent hints, bind a
state.statusstring to astatusbaror text block.Combine with success/error tones to give clear guidance after actions.
Theming and styling tweaks
Source: examples/widgets/spinner_demo.py + Styling & Themes
Create a custom
Themewith overrides (button colors, frame borders).Register and activate it via
app.renderer.theme_manager.register_theme(custom_theme)followed byapp.renderer.theme_manager.set_theme(custom_theme.name)during startup or in response to user preference toggles.Give elements a
classin the template (e.g.{% button class="danger" %}) and define that class in the theme — templates style by class, not by inline colour dicts.Snapshot the UI after theme changes to catch regressions.
Have a recipe to add? Drop a runnable snippet in examples/ and update this page so others can benefit. Refer back to Core Concepts for a deeper explanation of how these patterns plug into the runtime.