serhii.net

In the middle of the desert you can say anything you want

01 Apr 2026

NiceGUI notes

NiceGUI is freaking awesome.

Misc

Modularization

# somewhere
def create() -> None:
    @ui.page('/a')
    def page_a():
        with theme.frame('- Page A -'):
            message('Page A')
            ui.label('This page is defined in a function.')
			
from somewhere import create

# Example 2: use a function to move the whole page creation into a separate file
# in main.py
function_example.create()

Slots

OK I think I got it! In e.g. Select | Quasar Framework look for “slots” then you can use them thus1:

sel = ui.select(
	list(items),
	value=item,
)
with sel.add_slot("after"):
	ui.button(icon="description").props("flat no-caps").on(
		"click",
		lambda e: ui.navigate.to(f"/bp/{whatever}"),
	)

Table magic

	table = ui.table()#...
    with table.add_slot("body-cell-ColumnName"):
        with table.cell("ColumnName"):
			# button w/ cell text
            ui.button().props(""" 
                             :innerHTML="props.value" 
                             """).on(
                "click",
                js_handler="() => emit(props.row.SomeColumnName)",
                handler=lambda e: ui.navigate.to(f"/sth/{e.args}"), # e.args is cell content
            )

				# conditionals
                .props(""" flat no-caps
                             :label="props.value !== 'N/A' ? props.value : ''"
                             """)

… with button

with ui.link(target="/sd"):
	ui.button("Show", icon="description")

… without underline

with ui.link(target=target_link).classes("no-underline"):
	ui.item_label("look ma no underline!")

Multiple badges

Ref: Badge: Floating - Quasar Playground but that’s quasar-heavy. Strangely not mentioned in the official quasar docs either. After playing with this this is the python solution:


# remove the floating prop and add one of these classes.
# Removing the prop is useful if using e.g. both top-left and top-right
# then they are on an identicafl height
ui.badge(faiss_dist_str, color="light-blue-10").props(
	# "floating"
).classes("absolute-top-left")

Styling

Events

    ui.table(rows=rows).props("flat bordered").on(
        "row-click",
        lambda e: ui.navigate.to(f"/pr/{e.args[1]['ISIN']}"),
    )

The row-click event comes from quasar: https://quasar.dev/vue-components/table#qtable-api

APIs

async def show_new_quote():
    async with httpx.AsyncClient() as client:
        response = await client.get('https://zenquotes.io/api/quotes')
        quote = random.choice(response.json())['q']
    label.text = f'“{quote}”'

Snippets

Debugging long-running functions

From the FAQ:

# (..) app.on_startup(setup)
# def setup():
# if CFG.debug_mode:
import asyncio
loop = asyncio.get_running_loop()
loop.set_debug(True)
loop.slow_callback_duration = 0.05

Uploading files

ui.upload | NiceGUI

ui.upload(
	auto_upload=True, on_upload=lambda e: handle_upload(e)
).classes("max-w-full")

async def handle_upload(e: events.UploadEventArguments):
	with tempfile.NamedTemporaryFile(delete=False, prefix="uns") as tmp:
        save_path = Path(tmp.name)
        await e.file.save(save_path)
        return save_path
Bonus: FastAPI File upload

TODO

Focus

element.run_method('focus')

focuses the element, I could connect this to ui.keyboard | NiceGUI to do nice keyboard-centric focus of important fields!

Ref: element.run_method(‘focus’) not working · Issue #1092 · zauberzeug/nicegui (not documented elsewhere I could find)

Elsewhere

Validating in real time based on a pydantic BaseModel!: Real-Time Form Validation in Python Using Pydantic | by Aman Deep | Medium

Async hell

Using a variable globally defined outside a function creates issues. E.g. globally WHAT=‘ever’ and then inside a function. TODO better description.

Nel mezzo del deserto posso dire tutto quello che voglio.
comments powered by Disqus