NiceGUI Component Mechanics¶
NiceGUI components are Python objects that describe browser UI elements. A component constructor creates an element, constructor arguments configure its common behavior, and methods on the returned object expose styling, events, bindings, slots, and client-side capabilities.
This reference begins with those everyday component APIs, then describes the NiceGUI, Quasar, Vue, and browser layers beneath them. Page structure, typography, responsive composition, and scaling are covered separately in styling and customization.
Basic Components¶
Components are created from the ui namespace. Layout components are context managers, so nested Python blocks describe the element hierarchy:
from nicegui import ui
with ui.column().classes("gap-3"):
name = ui.input("Name", placeholder="Ada")
role = ui.select(
options={"admin": "Administrator", "reader": "Reader"},
value="reader",
label="Role",
).props("outlined dense")
ui.button("Save", on_click=lambda: ui.notify(f"Saved {name.value}"))
The NiceGUI component documentation is the index of available ui.* constructors. Each component page documents its Python parameters, values, callbacks, methods, and examples. The implementation for each wrapper is available in the NiceGUI element source tree.
Common Component Mechanics¶
Most NiceGUI elements inherit a common set of mechanics from Element; individual wrappers add component-specific properties and methods.
| Surface | What it represents | Source of supported values |
|---|---|---|
| Constructor arguments | NiceGUI's typed, Python-facing API for initial content, values, callbacks, validation, and common behavior | the component's page in the NiceGUI component documentation and its wrapper in the NiceGUI element source tree |
Properties such as .value and .options |
Python-side component state maintained by a particular wrapper | the component documentation and wrapper source; these properties are not universal Element APIs |
Wrapper methods such as set_options() |
NiceGUI state transitions that normalize Python data and schedule a client update | the component documentation and wrapper source |
.props(...) |
Quasar component props, Vue bindings, or HTML attributes serialized onto the frontend element | the API section of the wrapped component in the Quasar component documentation; NiceGUI element customization defines the bridge syntax |
.classes(...) |
CSS class names attached to the element | Tailwind's utility documentation for Tailwind classes; Quasar's breakpoint, spacing, visibility, and helper-class references for Quasar classes; or the application's own stylesheets for custom classes |
.style(...) |
Inline CSS declarations attached to the element | the MDN CSS reference |
Constructor callbacks and .on(...) |
NiceGUI callbacks and forwarded browser or Quasar events | the component's NiceGUI page first, then the Events section of its Quasar API; NiceGUI generic events documents .on(...) |
on_* methods |
Named event conveniences implemented by a specific NiceGUI wrapper, such as on_value_change |
the component documentation and wrapper source; there is no universal list that applies to every component |
bind_* methods |
synchronization between element properties and Python model properties | NiceGUI binding documentation and the wrapper's documented bindable properties |
add_slot(...) |
content inserted into a Quasar or Vue named slot | the Slots and Scoped Slots sections of the wrapped component's Quasar API |
run_method(...) |
invocation of a public method on the client component | the Methods section of the wrapped component's Quasar API |
Options And Values¶
options is component state rather than a universal styling mechanism. Components such as ui.select, ui.radio, ui.toggle, and ui.table define their own accepted option shapes and value semantics. For example, NiceGUI's ui.select documentation describes list and dictionary options, while the Select wrapper source shows how those Python values are normalized for Quasar.
Reading element.options accesses the wrapper's current Python-side options. Assigning or mutating options only changes browser state when the wrapper detects or sends an update. Component helpers such as set_options() encode that synchronization behavior and therefore belong to the wrapper's API rather than to Quasar's raw options prop.
Props¶
.props() writes props onto the frontend component:
ui.button("Archive").props("outline color=negative")
ui.select(["A", "B"]).props("dense options-dense")
For NiceGUI elements backed by Quasar, supported names and values come from the wrapped Quasar component's API. For example, the full QSelect API lists dense, options-dense, popup-content-class, events, slots, and methods. NiceGUI may already expose some of those features as typed constructor arguments or wrapper methods; the NiceGUI component page and source describe that higher-level behavior.
Property-String Format¶
NiceGUI's .props() string is parsed on the Python side by the tagged Props.parse() implementation. It accepts whitespace-delimited tokens in these forms:
| Form | Python-side result | Frontend meaning |
|---|---|---|
dense |
{"dense": True} |
a true boolean prop |
label=Chair |
{"label": "Chair"} |
a static string prop |
offset=[8, 8] |
{"offset": [8, 8]} |
a Python literal serialized as a value |
:label=someExpression |
{":label": "someExpression"} |
a JavaScript expression evaluated in the browser |
Quoted strings and bracketed or braced literals are parsed with Python's ast.literal_eval; unquoted values remain strings. Quote an expression when it contains whitespace or characters outside NiceGUI's unquoted-value grammar, or assign it through element.props[":name"] to avoid the string parser. Regular HTML attributes can pass through the same mechanism where the rendered element supports them. The NiceGUI element documentation defines the public bridge syntax.
Dynamic Props And Vue Bindings¶
The leading colon borrows Vue's v-bind shorthand, but NiceGUI elements are created with Vue's h() render function rather than compiled from a template. NiceGUI's tagged renderRecursively() implementation removes the colon, evaluates the value as JavaScript, and passes the result in the vnode's props object. For example:
corresponds conceptually to this Vue template:
The right-hand side is JavaScript, not Python. It may read browser globals, call functions, or construct arrays and objects, provided the receiving HTML element or Vue component accepts the resulting property. Inside a scoped slot, NiceGUI additionally makes that slot's current scope object available under the name props; outside a scoped slot, that name has no slot object to reference.
There is one important render-function distinction. Vue template syntax allows argument-less v-bind="object" to spread every key in an object. A literal .props("v-bind=someObject") token is not compiled as a directive by NiceGUI's render-function path and does not spread the object. Use a raw add_slot(..., template=...) Vue template when a slot contract requires whole-object binding, or bind the documented fields individually. Vue's render-function reference defines the equivalent programmatic form as passing or spreading those keys in the object supplied to h().
Controlled Values And Model Events¶
Vue component v-model expands to a value prop plus an update listener. For the common modelValue contract, that means modelValue and update:modelValue, as defined by the Vue component v-model guide and its tagged compiler transform.
NiceGUI can express a deliberately one-way controlled value with a dynamic prop and handle the corresponding proposal separately. For example, ui.number follows QInput's common modelValue contract:
number_editor = ui.number()
number_editor.props(':model-value="props.value"').on(
"update:model-value",
handler=apply_proposal,
js_handler="(value) => emit(value)",
)
This pattern is useful inside a scoped slot or whenever Python must authorize a change before reasserting component state. The dynamic prop displays the current client-side projection; the listener sends an edit proposal to Python instead of assigning into the source object in JavaScript. Use an ordinary NiceGUI value binding when the wrapper's two-way value model already matches the requirement.
ui.input Wrapper Exception¶
In NiceGUI 3.16.0, ui.input is a NiceGUI client wrapper around QInput rather than a direct QInput element. The tagged input.js component defines its controlled prop and event as value and update:value, not model-value and update:model-value. It also adds a static empty value prop. Remove that prop before adding a row-scoped dynamic value:
name_input = ui.input().props(remove="value")
name_input.props(':value="props.value"').on(
"update:value",
handler=apply_proposal,
js_handler="(value) => emit(value)",
)
Using :model-value="props.value" leaves this wrapper's own value unchanged, so repeated text inputs in a scoped table slot render blank. ui.number is a direct QInput specialization and therefore uses model-value and update:model-value as shown above. Check each NiceGUI wrapper's VALUE_PROP and client component before assuming the underlying Quasar model contract is exposed unchanged.
An update:model-value listener receives the component's emitted model value, whose shape is component-specific. A custom listener also bypasses normalization that a wrapper's built-in value handler may perform. For example, the Quasar input beneath ui.number can emit numeric text, so the Python proposal handler must perform authoritative numeric conversion.
NiceGUI serializes ui.select options into QSelect objects shaped like {value: index, label: option_label} and normally maps the selected object back to the corresponding Python option. A custom js_handler receives that object before NiceGUI's Python-side conversion. When list values and labels are intentionally identical, forward option.label; otherwise emit the index and resolve it against the authoritative Python options rather than trusting a browser-supplied label.
Classes And Styles¶
.classes() adds class names to the rendered element:
ui.label("Account").classes("text-lg font-semibold text-slate-800")
ui.row().classes("w-full items-center gap-4")
NiceGUI includes Tailwind-compatible utility styling, so names such as flex, gap-4, w-full, and text-slate-800 are defined by Tailwind. The complete categorized list is the Tailwind CSS documentation; its utility-class guide explains variants, responsive prefixes, and arbitrary values. NiceGUI can alternatively run with a selected UnoCSS preset, whose compatibility limits are documented under NiceGUI's UnoCSS engine.
Quasar publishes its classes by category rather than through a single style index. The breakpoint reference defines viewport thresholds, the spacing reference lists the q-p* and q-m* permutations, the visibility reference covers responsive and platform visibility, and the other helper classes reference covers pointer, scrolling, sizing, rotation, and border helpers. Application-defined class names are supported when their CSS is loaded with ui.add_css, static assets, or page head content. .style() accepts CSS declarations directly, separated by semicolons.
Events And on_* Methods¶
Callbacks supplied by a constructor are NiceGUI's documented event surface:
ui.input("Search", on_change=lambda event: print(event.value))
ui.button("Refresh", on_click=lambda: print("refresh"))
Some wrappers also expose named registration methods such as on_value_change. Their availability and event argument type are component-specific and are documented on the NiceGUI component page or in its wrapper source.
.on() is the generic event bridge for events without a dedicated Python convenience API:
For Quasar-backed elements, the component API's Events section is the authoritative list of emitted event names and payloads. Native browser events are documented in the MDN event reference. NiceGUI's generic event documentation defines the public .on() API.
Mapping Quasar Event Names¶
Quasar documents each component event under its Events API entry. Use the documented kebab-case name with .on(). NiceGUI's tagged event_type_to_camel_case() helper converts the event name before the first modifier dot to the camelCase form emitted by the component; its frontend renderer then creates Vue's onXxx listener prop. These forms therefore map to the same event path:
| Quasar API name | NiceGUI registration | Vue runtime listener |
|---|---|---|
popup-show |
.on("popup-show", ...) |
onPopupShow |
input-value |
.on("input-value", ...) |
onInputValue |
update:model-value |
.on("update:model-value", ...) |
onUpdate:modelValue |
Vue component events are notifications emitted by the direct component; unlike DOM events, they do not bubble through component ancestors. Prefer a NiceGUI constructor callback, binding, or named wrapper method when one already owns the same behavior. In particular, use on_change or a value binding instead of registering another update:model-value listener unless the lower-level model event is specifically required.
Reading Event Payloads¶
The Quasar event's documented params define the positional arguments received by the listener. NiceGUI serializes those arguments and exposes them as GenericEventArguments.args in Python. If exactly one argument is emitted, NiceGUI presents that value directly; multiple emitted arguments remain a list in their documented order.
For example, the version-matched QSelect event API defines add as one details object containing index and value:
def handle_add(event) -> None:
print(event.args["index"], event.args["value"])
item_select.on("add", handle_add, args=["index", "value"])
The args parameter controls transport, not Quasar's event signature:
args value |
Data sent to Python |
|---|---|
None |
all JSON-serializable attributes of every emitted argument |
[] |
no event arguments |
["index", "value"] |
only those attributes from a one-object event argument |
[[], ["name"], None] |
for a three-argument event: none from the first, name from the second, and all of the third |
Primitive values and arrays are forwarded as values rather than filtered by attribute name. Browser objects, DOM nodes, component references, functions, and cyclic structures are not meaningful server payloads; select the small serializable subset the Python handler actually needs.
Transforming Events In The Browser¶
js_handler receives the original Quasar or browser event arguments in the browser. Calling NiceGUI's injected emit(...) forwards only the transformed arguments to the Python handler:
item_select.on(
"add",
handler=lambda event: print(event.args),
js_handler="(details) => emit({index: details.index, value: details.value})",
)
Omit the Python handler for a client-only action, or omit js_handler to use NiceGUI's default (...args) => emit(...args) forwarding behavior. Since NiceGUI 2.18.0, both can be supplied together. A js_handler may also decide not to call emit, in which case no Python callback runs for that occurrence.
Events that pass imperative JavaScript callbacks require special care. For example, QSelect's filter event emits an input string plus doneFn and abortFn functions. Those functions cannot be serialized for later use by Python. Use NiceGUI's wrapper-supported filtering API, or consume such callbacks synchronously in browser-side JavaScript; do not treat them as ordinary server payloads.
Server-Authoritative Edit Proposals¶
Treat values received from the browser as proposals, even when Quasar validation or input constraints have already run. Attach the listener to the component that emits the event, use js_handler to send only the identity and serializable values Python needs, and validate the field allowlist, types, ranges, permissions, record existence, and persistence constraints in Python. The browser may keep temporary editor state, but it is not the source of truth.
Choose when proposals cross the client-server boundary according to the interaction:
- Use
update:model-valuefor discrete editors such as selects, switches, and checkboxes. - For text and numeric inputs accepted during typing, use the component's documented
debounceprop to avoid a server round trip for every keystroke. - For an explicit save/cancel workflow, keep a local draft in a dialog or popup and emit one proposal on save.
- During asynchronous persistence, disable the editor or expose a busy state. Add an entity version or another optimistic-concurrency check when multiple clients can edit the same record.
After validation, pass accepted values to the authoritative model and persistence boundary. See bindable dataclasses for projection, rollback, and refresh mechanics, and editable tables for the QTable-specific form of this pattern.
Modifiers And High-Frequency Events¶
Dot suffixes use Vue's event and key modifier rules:
field.on("keydown.enter", submit)
field.on("click.stop", handle_click)
viewport.on("scroll.passive", handle_scroll, throttle=0.1)
NiceGUI separates listener options such as capture, once, and passive, event modifiers such as stop, prevent, and self, and key filters such as enter. The tagged EventListener.to_dict() implementation performs that classification before the frontend applies Vue's withModifiers() and withKeys() helpers. throttle, leading_events, and trailing_events regulate messages sent to Python; they do not throttle a client-only js_handler that never calls emit.
Custom Vue Components¶
When NiceGUI's wrappers and the documented Quasar extension points cannot express a component, subclass ui.element and pair it with a Vue component. Start from NiceGUI's custom Vue component example, keeping Python responsible for the server-facing state and event contract.
For a component with npm dependencies, bundle the frontend module and pass its ESM module name and bundled file path through the esm parameter on the Python element subclass. NiceGUI adds that module to the page import map. The signature pad example and node module integration example demonstrate the package and bundling boundary.
Treat the generated JavaScript and CSS as package data in executable builds. PyInstaller or Nuitka configuration must include those assets, and the packaged artifact must be checked for successful module loading rather than only for process startup. Do not introduce a custom Vue component merely to avoid a supported NiceGUI constructor, Quasar prop, event, slot, or public method.
Framework Boundary Model¶
A NiceGUI component is not a Python-rendered HTML fragment. Customization passes through several owners:
| Layer | Owns | Inspect when |
|---|---|---|
| NiceGUI Python wrapper | constructor arguments, Python value normalization, validation, bindings, event callbacks, and update helpers | behavior may already have a typed Python API or wrapper-specific state rules |
| NiceGUI element bridge | serialized props, classes, styles, events, slots, and frontend method calls | mapping a supported Vue or Quasar feature through NiceGUI |
| Quasar Vue component | documented props, emitted events, named slots, public methods, popup behavior, accessibility, and internal state | the NiceGUI constructor does not expose a required component feature |
| Vue and browser runtime | reactivity, rendered DOM, teleported content, CSS cascade, fonts, and static assets | diagnosing placement, asset loading, or content rendered outside the element subtree |
Treat the generated DOM beneath a Quasar component as private implementation detail. Work through the highest owning layer that expresses the requirement.
API Mapping Across Layers¶
| Requirement | NiceGUI surface | Underlying mechanic |
|---|---|---|
| Wrapper-supported value or behavior | constructor argument, binding, or helper such as set_options() |
Python normalizes state and synchronizes the component |
| Additional Quasar option | .props(...) |
values become props on the wrapped Vue component |
| Browser or Quasar notification | constructor callback or .on(...) |
an emitted frontend event is forwarded to a Python handler |
| Semantic insertion point | add_slot(...) or a wrapper-specific slot API |
content renders in a named Vue slot |
| Imperative frontend action | a NiceGUI helper or run_method(...) |
NiceGUI invokes a public method on the client component |
| Page placement or appearance | .classes(...), .style(...), or an application stylesheet |
CSS applies to the rendered element; detached content needs its own class hook |
Constructor data remains in Python, Quasar props cross through .props(), emitted events cross through callbacks or .on(), and named Vue slots cross through NiceGUI's slot API. A Vue example in the Quasar documentation therefore maps to several distinct NiceGUI surfaces rather than to one copied template.
State And Event Flow¶
Server-driven changes and user-driven changes cross a client-server boundary:
- Python creates the wrapper and serializes initial state to the client.
- Vue renders the Quasar component from those props and slots.
- A browser interaction causes Quasar to update client state or emit an event.
- NiceGUI forwards registered events to Python handlers.
- Python mutations return through bindings, wrapper helpers, or an explicit
update().
Wrapper helpers and bindings preserve NiceGUI's value model and schedule the corresponding client update. Directly changing a plain Python collection or constructing a raw JavaScript object does not itself imply that the client receives the change.
Detached Content And Assets¶
Some Quasar components render menus, dialogs, tooltips, and similar content outside the field or trigger's DOM subtree. A descendant CSS selector beneath the Python-created element will not reach that content. Component APIs expose props such as popup-content-class for assigning a separate class hook to detached content.
Icons and other externally defined visuals add another boundary: a valid Quasar icon name identifies an asset but does not load its font or stylesheet. Confirm both the naming convention and the application-level asset registration.
Versioned Sources¶
The exact public surface depends on both the installed NiceGUI version and the Quasar version bundled with it. NiceGUI's tagged package.json records that pairing. The component details below describe NiceGUI 3.16.0 with Quasar 2.18.5, as declared by NiceGUI v3.16.0 frontend dependencies.
Four source levels answer different questions:
| Source | Information it defines |
|---|---|
| NiceGUI component documentation | documented Python constructors, callbacks, methods, and examples |
| NiceGUI wrapper source at the installed tag | normalization, validation, stored properties, bindings, updates, and the wrapped frontend component |
| Quasar component API at the bundled tag | accepted props, emitted events, named slots, public methods, accessibility behavior, and warnings |
| Quasar component source at the bundled tag | detailed runtime behavior behind that public API |
Links to main, dev, or the latest hosted documentation can describe a newer API than the installed package. Tagged NiceGUI and matching quasar-v<version> links provide the version-specific definition.
Using Slots In NiceGUI¶
A NiceGUI element is the Python-side representation of a browser component. Many elements wrap Quasar Vue components, whose insertion points are exposed as slots. A simple container normally uses one default slot; more complex components expose named slots such as prepend, append, option, header, or body-cell-*. The available names and their contracts belong to the wrapped component, so verify them in the version-matched Quasar documentation.
NiceGUI creates a default slot for every element. Entering an element as a context manager enters that default slot, and entering element.add_slot(name) selects a named slot. NiceGUI keeps the active slots on a task-local stack; each element constructed inside the with block becomes a child of the innermost active slot.
These mechanics are defined by the tagged Element.add_slot() implementation, the Slot context manager, and NiceGUI's context-managed scoped-slot examples.
Prefer Python-Owned Composition¶
Use NiceGUI context managers and ui.* elements for slot structure whenever they can represent the required element tree. Keep values, mappings, validation, permissions, event handling, and authoritative state transitions in Python. This preserves element identity, typed wrapper APIs, lifecycle cleanup, test visibility, and the normal NiceGUI update path.
Use the narrowest browser-side expression for state that exists only while Quasar renders a scoped slot. A dynamic prop such as :label="props.value" may project that value into a NiceGUI element without moving the surrounding structure or business rules into JavaScript. When Python needs a browser-owned value, emit the smallest serializable proposal to a Python handler and validate it there.
Escalate to add_slot(name, template) only when the slot contract requires client-side structure that context-managed NiceGUI elements cannot preserve, such as a browser-side v-for, a variable number of sibling roots, or Vue's object form of v-bind for a Quasar interaction bundle. Keep raw templates small, use documented scoped props, and do not duplicate authoritative application logic in JavaScript.
Context-Managed NiceGUI Elements¶
Ordinary NiceGUI elements can populate slot content:
Nested context managers express the component hierarchy while preserving NiceGUI element identity, event registration, updates, deletion, and test visibility.
Scoped Props On The Client¶
A scoped slot is a function whose argument is supplied by the component that renders the slot. Vue calls that argument the slot props; props is only NiceGUI's chosen local name for it. Since NiceGUI 3.5.0, context-managed NiceGUI elements inside a scoped slot receive the current slot-props object as their frontend render context.
The general .props() grammar and dynamic binding path are described under Props. In this context, the current scope object can be referenced by dynamic properties and JavaScript event handlers. For example:
corresponds conceptually to this Vue template:
Static .props() values do not have access to the slot scope. Only colon-prefixed expressions and NiceGUI JavaScript event handlers are evaluated with props in scope.
Which props.* Names Exist¶
There is no global catalog of props.* attributes. The owner of each named slot chooses the keys it passes when invoking that slot, so the available names can differ between components and between slots on the same component. Find them in this order:
- Open the wrapped component's version-matched Quasar API and inspect the Slots entry for the exact named slot.
- Use the slot's
scopetable as the public contract, including each value's type and whether it is data, state, or a callable. - Inspect the version-matched Quasar source only when the API does not explain a bundle's contents or runtime behavior.
For example, the QSelect option slot API at Quasar 2.18.5 exposes:
| Expression | Meaning |
|---|---|
props.index |
index in the options array |
props.opt |
original option from the options prop |
props.label |
label after option-label processing |
props.html |
whether the option content is marked as HTML |
props.selected |
whether this option is selected |
props.focused |
whether this option is the focused menu option |
props.toggleOption |
function that adds or removes an option from the model |
props.setOptionIndex |
function that changes the focused option index |
props.itemProps |
object of computed props and listeners intended for the root QItem |
The tagged QSelect implementation constructs itemProps with values such as clickable, active, activeClass, manualFocus, focused, disable, tabindex, dense, dark, role, aria-selected, id, onClick, and, when applicable, onMousemove. It is a behavior and accessibility bundle, not the original option object. Other QSelect slots expose different scopes: no-option only documents inputValue, while selected-item documents selection-oriented keys such as index, opt, removeAtIndex, toggleOption, and tabindex. A QTable body-cell slot's props.value is valid because QTable supplies value; that name should not be assumed in a QSelect option slot.
Sending Scoped Values To Python¶
NiceGUI also places the current slot object in scope while evaluating a js_handler. Use the event bridge's emit(...) function to select or transform JSON-serializable values before the Python callback runs:
ui.button("Inspect").on(
"click",
handler=lambda event: print(event.args),
js_handler="() => emit({index: props.index, label: props.label})",
)
Scoped props exist only in the browser render context. They are not Python variables and cannot be read by a Python callback until a JavaScript handler emits the required values. Treat innerHTML, v-html, and raw template interpolation as untrusted HTML unless the source is explicitly sanitized.
Slot Contracts¶
Replacing default slot content also replaces the wrapped component's default rendering. Documented slot-prop bundles can carry behavior as well as data. For example, a QSelect option slot binds props.itemProps to its root item; without that binding, the custom row can lose click selection, disabled state, focus, active state, and keyboard navigation. Quasar's virtual-scroll contract expects one root element per item unless additional siblings carry its documented marker class.
ui.select¶
Versioned Source Definitions¶
- NiceGUI documentation:
ui.selectdocumentation source atv3.16.0 - NiceGUI source code:
Selectimplementation atv3.16.0 - Quasar documentation:
QSelectdocumentation source at2.18.5 - Quasar source code:
QSelectimplementation at2.18.5
Layer Ownership¶
NiceGUI's Select wraps Quasar QSelect but owns important Python-side behavior. Its constructor handles options, labels, values, change callbacks, input filtering, new-value modes, multiple selection, clearing, validation, and key generation. Use those constructor parameters before adding equivalent Quasar props manually.
Exposed Surfaces¶
- The NiceGUI constructor exposes
options,label,value,on_change,with_input,new_value_mode,multiple,clearable,validation, andkey_generator. .props()carries additional documentedQSelectbehavior such as field design, chips, option density, popup classes, popup positioning, and menu/dialog behavior..classes()attaches structural width, placement, and other CSS utilities to the field element.- Named slots provide prepend, append, loading, no-option, selected, and option content.
- Scoped-slot props retain Quasar's selection and keyboard behavior when option content is replaced.
Example: Custom Menu Options With A Scoped Slot¶
QSelect supplies each option as props.opt, its processed label as props.label, and its interaction contract as props.itemProps. Because the complete interaction bundle needs Vue's object form of v-bind, use a raw slot template for the root item:
from nicegui import ui
item_select = ui.select(
options={"chair": "Chair", "desk": "Desk", "lamp": "Lamp"},
label="Item",
value="chair",
clearable=True,
with_input=True,
).props("outlined options-dense")
with item_select.add_slot("prepend"):
ui.icon("search")
item_select.add_slot(
"option",
r"""
<q-item v-bind="props.itemProps">
<q-item-section avatar>
<q-icon name="inventory_2" />
</q-item-section>
<q-item-section>
<q-badge :label="props.label" outline color="primary" />
</q-item-section>
</q-item>
""",
)
The prepend slot uses context-managed NiceGUI elements because it needs no scoped object spread. The raw option template is compiled by Vue, so v-bind="props.itemProps" forwards every computed property and listener to QItem; the badge reads the processed browser-side label. Keep that binding on the root item so the custom rendering retains the option's interaction and accessibility wiring.
Behavioral Caveats¶
These caveats are distilled from the four version-matched sources above:
- NiceGUI accepts a list of values or a dictionary mapping values to labels. Do not assume the Python options model is the same as Quasar's JavaScript object-array examples.
- After mutating
options, callupdate()or useset_options()so the client receives the change. new_value_modeenables input automatically. For dictionary options withadd, NiceGUI requires akey_generator.- A multiple select has a list value. NiceGUI normalizes a non-list initial value, but application state should still use the intended list shape.
map-optionshas a Quasar performance cost. Do not add it to NiceGUI's mapped options without confirming that the wrapper's value translation requires it.display-value-htmlandoptions-htmlcan create cross-site scripting risk. When usingselected,selected-item, oroptionslots, the application owns sanitization.- A custom
optionslot must bindprops.itemPropsto its rootQItemso click, focus, active, disabled, and keyboard behavior remain connected. - Custom option slots use virtual scrolling. When one option renders multiple sibling elements, Quasar requires
q-virtual-scroll--with-prevon every additional sibling. - Buttons placed in
before,after,prepend, orappendfield slots do not propagate clicks to the parent. A submit button in one of those slots needs its own submit handler. QSelectrenders its popup outside the field. Style it throughpopup-content-class; do not assume a descendant selector beneath the field will reach it.- Quasar switches between menu and dialog popup behavior by platform. Verify forced
behavior=menucarefully on iOS when input filtering is enabled.
.on() and run_method() address events and methods defined by the installed Quasar API. NiceGUI's on_change, set_options(), value bindings, and is_showing_popup provide wrapper-managed equivalents for their respective behaviors.
ui.icon¶
Versioned Source Definitions¶
- NiceGUI documentation:
ui.icondocumentation source atv3.16.0 - NiceGUI source code:
Iconimplementation atv3.16.0 - Quasar documentation:
QIcondocumentation source at2.18.5 - Quasar source code:
QIconimplementation at2.18.5
Layer Ownership¶
NiceGUI's Icon is a thin QIcon wrapper. Its constructor exposes name, size, and color; the source forwards these to a q-icon element. Use Quasar's icon naming and asset rules for anything beyond those parameters.
Exposed Surfaces¶
- The application-loaded icon family determines which icon names can render.
ui.icon()accepts the documented icon name, size, and color..props()carries supportedQIconprops such asleft,right, and a custom render tag..classes()controls structural placement and can attach application-defined visual variants.- Static stylesheets define Material Symbol axes, state variants, custom webfonts, and repeated effects.
Example¶
from nicegui import ui
ui.icon(
"sym_o_home",
size="1.5rem",
color="primary",
).classes(
"app-symbol-filled shrink-0"
).tooltip(
"Home"
)
Behavioral Caveats¶
These caveats are distilled from the four version-matched sources above:
- Material icon names use snake case. Material variants use prefixes such as
o_,r_,s_,sym_o_,sym_r_, andsym_s_. - Other icon families have their own prefixes and require their webfont or stylesheet to be loaded. A valid name does not load the corresponding asset.
sizeaccepts CSS units or Quasar sizes such asxs,sm,md,lg, andxl. Quasar implements icon sizing throughfont-size.- Icon color inherits text color unless the
colorprop or a CSS color overrides it. - Material Symbol variable axes apply to webfont icons, not static SVG icon exports.
- Quasar also supports SVG path strings,
svguse:references, andimg:URLs. Confirm the exactQIconname format and mount path before generating one of these forms. QIconrenders witharia-hidden="true". For an action, use a semantic control such asui.button(icon=..., on_click=...)and put the accessible name on that control; a tooltip is supplementary.- Prefer
ui.icon(...).tooltip(...)over manually constructing tooltip slot markup when NiceGUI's method covers the visual hint.
Related Reference Index¶
- NiceGUI component documentation: Python constructors, callbacks, bindings, and wrapper methods
- NiceGUI
Elementdocumentation: common props, classes, styles, hierarchy, updates, and client methods - NiceGUI generic events:
.on(), event arguments, JavaScript handlers, and throttling - NiceGUI binding documentation: one-way and two-way Python property binding
- Quasar component documentation: per-component props, events, slots, and methods
- Quasar breakpoints: viewport names and pixel thresholds
- Quasar spacing classes: padding and margin class syntax and permutations
- Quasar visibility classes: responsive, platform, orientation, and print visibility
- Quasar helper classes: pointer, scrolling, sizing, rotation, and border helpers
- Tailwind CSS documentation: complete utility-class categories and variant syntax
- MDN CSS reference: CSS properties accepted by
.style()and application stylesheets - MDN event reference: native browser event names and behavior