18 KiB
NiceGUI Component Mechanics
Use this reference to understand how customization crosses the NiceGUI Python wrapper, Quasar component, Vue runtime, and browser DOM. It owns constructor behavior, prop translation, events, bindings, slots, frontend methods, detached content, and component-specific caveats. For themes, utility classes, CSS properties, responsive page composition, and other cosmetic work, load visual styling and CSS.
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.
How The APIs Map
Use this map after confirming the exact API against the installed NiceGUI and bundled Quasar versions:
| 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 |
Do not copy a Vue template into Python. Translate each part according to its owner: constructor data stays in Python, Quasar props go through .props(), emitted events go through callbacks or .on(), and named Vue slots go through NiceGUI's slot API.
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().
Use wrapper helpers and bindings when available because they preserve NiceGUI's value model. Directly changing a Python collection or constructing a raw JavaScript object does not 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. Use the component's documented popup or content class prop, then style that application-owned class separately.
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.
Component Customization Workflow
Research the target component before generating code or CSS. Do not rely on a remembered NiceGUI or Quasar API, and do not mix source versions.
Establish The Version Pair
- Read the target project's lockfile or installed package metadata to identify its exact NiceGUI version.
- Open
package.jsonat that NiceGUI tag and read the exactquasardependency version. - Use the NiceGUI tag for both NiceGUI sources and the matching
quasar-v<version>tag for both Quasar sources.
The curated component sections below use NiceGUI 3.16.0 and Quasar 2.18.5. The pairing comes from NiceGUI v3.16.0 frontend dependencies. Repeat the version check when the target application uses another NiceGUI release. Never infer compatibility from Quasar's latest release or use NiceGUI main with Quasar dev.
Research Four Sources
Review these sources in order for the selected version pair:
- NiceGUI documentation: identify the supported Python API and documented examples for the component.
- NiceGUI source code: inspect constructor normalization, validation, props, bindings, events, helpers, and the wrapped frontend component.
- Quasar documentation: identify the wrapped component's public props, slots, events, methods, accessibility behavior, and documented warnings.
- Quasar source code: verify how those public APIs behave, especially popup mounting, model translation, event flow, rendering, and public methods.
Use current upstream sources only when the target version is unavailable, and state that fallback explicitly. If the installed package differs from its tag, follow the installed implementation and record the difference.
Apply The Findings
For every component section:
- Link the four version-matched sources under Research Sources.
- Summarize which layer owns the behavior under Ownership Result.
- Order the supported customization surfaces from highest-level NiceGUI API to lower-level Quasar or CSS mechanisms.
- Include an example only after the owning APIs are established.
- Curate a short caveat list from the four sources. Keep only constraints that change implementation, security, accessibility, performance, or testing decisions.
If the requirement is purely visual after this ownership check, continue in visual styling and CSS.
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 Context-Managed NiceGUI Elements
Build slot content with ordinary NiceGUI elements by default:
name_input = ui.input("Name")
with name_input.add_slot("prepend"):
ui.icon("person")
Use nested context managers to express the component hierarchy. This preserves NiceGUI element identity, event registration, updates, deletion, and test visibility. Pass a raw Vue template to add_slot(name, template) only when the slot requires client-side structure that ordinary NiceGUI elements cannot express cleanly, such as a v-for that creates a variable number of sibling elements.
Use Scoped Props On The Client
A scoped slot receives a props object from its owning Vue component. Since NiceGUI 3.5.0, NiceGUI elements inside a scoped-slot context can reference that object in dynamic .props() expressions and JavaScript event handlers:
- use
.props(":label=props.value")or another component-supported prop to display a scoped value - use
.props("v-bind=props.itemProps")when the slot provides a bundle of required attributes and handlers - use
.on(..., js_handler="... emit(...)", handler=...)to transform and send serializable scoped values to Python
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.
Preserve The Slot Contract
Replacing default slot content also replaces the wrapped component's default rendering. Preserve any documented slot-prop bundle that carries behavior. For example, a QSelect option slot must bind props.itemProps to its root item; otherwise the custom row can lose click selection, disabled state, focus, active state, and keyboard navigation. Keep one root element per virtual-scroll item unless the component documents how to mark additional siblings.
ui.select
Research Sources
- 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
Ownership Result
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.
Customization Order
- Use
options,label,value,on_change,with_input,new_value_mode,multiple,clearable,validation, andkey_generatorthrough the NiceGUI constructor. - Use
.props()for additional documentedQSelectbehavior such as field design, chips, option density, popup classes, popup positioning, or menu/dialog behavior. - Use
.classes()and Tailwind for the field's structural width and placement. - Use named slots for prepend, append, loading, no-option, selected, or option content when props are insufficient.
- Preserve the documented scoped-slot props when replacing option content so Quasar retains selection and keyboard behavior.
Example: Custom Menu Options With A Scoped Slot
QSelect supplies each option as props.opt and its interaction contract as props.itemProps. NiceGUI elements can consume both inside the slot context without a raw Vue template:
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")
with item_select.add_slot("option"):
with ui.item().props("v-bind=props.itemProps"):
with ui.item_section().props("avatar"):
ui.icon("inventory_2")
with ui.item_section():
ui.badge().props(":label=props.opt.label outline color=primary")
The prepend slot adds content around the field. The scoped option slot replaces every menu row with context-managed NiceGUI elements; the badge reads the browser-side option label through a dynamic Quasar prop. Keep v-bind=props.itemProps on the root ui.item() so the custom rendering retains the option's interaction and accessibility wiring.
Curated 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 rootui.item()so 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.
Use .on() or run_method() only after confirming the event or method in the installed Quasar API. Prefer NiceGUI's on_change, set_options(), value bindings, and is_showing_popup when they cover the behavior.
ui.icon
Research Sources
- 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
Ownership Result
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.
Customization Order
- Choose an icon family that is actually loaded by the application.
- Pass the documented icon name, size, and color to
ui.icon(). - Use
.props()for supportedQIconprops such asleft,right, or a custom render tag. - Use
.classes()for structural placement and an application class for stable visual variants. - Use a static stylesheet for Material Symbol axes, state variants, custom webfonts, or 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"
)
.app-symbol-filled {
font-variation-settings:
"FILL" 1,
"wght" 400,
"GRAD" 0,
"opsz" 24;
}
Curated 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.
Completion Check
Before accepting a special-component customization:
- Record the target NiceGUI version and its declared Quasar version.
- Link the version-matched NiceGUI documentation and source code.
- Link the version-matched Quasar documentation and source code.
- Identify constructor arguments, Quasar props, slots, Tailwind classes, and stylesheet rules separately.
- Confirm detached popup or external asset behavior where applicable.
- Keep the caveat list traceable to the four researched sources.
- Test keyboard interaction, focus, labels, and tooltips.
- Test the supported mobile, landscape desktop, and portrait desktop viewports.