Setup app

These examples expect that the user has setup the initial application like the following code snippet. The examples should be inserted into the home_page function.

It is recommended to use a virtual environment like poetry or pipenv.

Create a main.py file

from uiwiz import ui, UiwizApp
import uvicorn

app = UiwizApp()

@app.page("/")
async def home_page():
    ui.label("Hello world")

if __name__ == "__main__":
    uvicorn.run("main:app", reload=True)

Run it

python main.py

Text elements

Labels for inputs

ui.label("Create a label")

Link

ui.link("UiWizard", "https://ui-wizard.com")
UiWizard
A link will often go in a menu
ui.link("UiWizard", "https://ui-wizard.com").classes("btn btn-ghost drawer-button font-normal")
with ui.element("ul").classes("menu menu-horizontal menu-md"):
    with ui.element("li"):
        ui.link("UiWizard", "https://ui-wizard.com")
UiWizard

Markdown

Render our favourite MD
ui.markdown("""This is **Markdown**.""")

This is Markdown.

HTML

Render raw html string. This should be used with care as it can be used to inject code if the content is user generated!
ui.html("UiWizard <strong>STRONG</strong>")
UiWizard STRONG

Control elements

Button

from uiwiz import ui, UiwizApp
app = UiwizApp()

@app.ui("/click/button")
def click_button():
    ui.toast("Clicked")

ui.button("Click me").on_click(click_button, swap="none")

Checkbox

box = ui.checkbox(name="checkbox")
ui.label(text="Checkbox", for_=box)

Checkbox

ui.datepicker(name="datepicker", value=datetime.now(timezone.utc))

Toggle

toggle = ui.toggle(name="key_name")
ui.label("Enable feature", toggle)

Dropdown

items = ["UiWizard", "HTMX"]
ui.dropdown(name="selection", items=items, placeholder="Placeholder")

Range

ui.range(name="Range", min=0, max=10, value=0, step=2)

Radio

with ui.col():
    with ui.row():
        wiz = ui.radio(name="radio", value="UiWizard")
        ui.label(text="UiWizard", for_=wiz)
    with ui.row():
        htmx = ui.radio(name="radio", value="HTMX")
        ui.label(text="HTMX", for_=htmx)

Input

from uiwiz import ui, UiwizApp
app = UiwizApp()

class InputModel(BaseModel):
    input: str

@app.ui("/change/input")
def change_input(input_model: InputModel):
    ui.element(content=input_model.input)

ui.input(name="input", placeholder="Text").on("input", change_input, target=lambda: output.id)
output = ui.element()

Text area

from uiwiz import ui, UiwizApp
app = UiwizApp()

class InputModel(BaseModel):
    input: str

@app.ui("/change/input")
def change_input(input_model: InputModel):
    ui.element(content=input_model.input)

ui.textarea(name="input", placeholder="Text").on("input", change_input, target=lambda: output.id)
output = ui.element()

Upload

The upload is limited to 2048 bytes as it could be used to abuse server

from uiwiz import ui, UiwizApp
from fastapi import UploadFile
app = UiwizApp()

async def handle_upload(upload: UploadFile):
    data = upload.read()
    ui.toast(data)

ui.upload(name="upload").on_upload(on_upload=handle_upload, swap="none").attributes["accept"] = ".txt"

Forms

The name of the input element must match the name of the model attribute. Notice that the form will indicate the input, which failed validation. This works by leveraging pydantic and fastapi, which automatically sends a 422 response with the fields that failed validation.

from pydantic import BaseModel, Field
from uiwiz import ui, UiwizApp
app = UiwizApp()

class FormData(BaseModel):
    desc: str = Field(min_length=4)
    age: int
    # ..

# Without the @app.ui a hash of the function is
# used instead for the endpoint instead
@app.ui("/form/inputdata")
async def handle_form_request(input_form: FormData):
    ui.toast(str(input_form)).success()

with ui.form().on_submit(handle_form_request, swap="none"):
    ui.input("Description", name="desc")
    ui.input("Age", name="age")
    ui.button("submit")

It is also possible to have UiWiard create the form from a model

class FormData(BaseModel):
    desc: str = Field(min_length=4)
    age: int
    # ..

@app.ui("/form/inputdata")
async def handle_form_request(input_form: FormData):
    ui.toast(str(input_form)).success()

ui.modelForm(FormData).on_submit(handle_form_request)

It is also possible to customize the inputs from the model

class Base(BaseModel):
    desc: Annotated[str, UiAnno(ui.textarea)] = Field(min_length=4)
    age: int

ui.modelForm(
    Base,
    age={
        "ui": ui.dropdown,
        "placeholder": "Select",
        "items": [1, 2, 3, 4]
    }
).on_submit(handle_form_request)

Indicators

ui.spinner()
ui.spinner().ball()
ui.spinner().bars()
ui.spinner().infinity()
ui.spinner().dots()
ui.spinner().infinity().extra_small()
ui.spinner().infinity().small()
ui.spinner().infinity().medium()
ui.spinner().infinity().large()

The indicators can be used on actions performed by any element that requires a web request transaction

with ui.button("Request").on_click(handle_spinner, swap="none") as requester:
    ui.spinner(requester).infinity().medium()

Data and layout

Tables

data = [
    {"col1": "dat1", "col2": "dat2"},
    {"col1": "dat1", "col2": "dat2"}
]
df = pd.DataFrame(data)
ui.table.from_dataframe(df)
col1col2
dat1dat2
dat1dat2
Aggrid - use this as a reference for implementing new client side components
The theme is fully integrated into the DaisyUI themes. It will even work with custom themes!
data = [
    {"col1": "dat1", "col2": "dat2"},
    {"col1": "dat1", "col2": "dat2"}
]
df = pd.DataFrame(data)
ui.aggrid(df)

Tabs

with ui.tabs():
    with ui.tab("Tab 1"):
        ui.element(content="This is tab 1")
    with ui.tab("Tab 2"):
        ui.element(content="This is tab 2")
This is tab 1
This is tab 2

Toast

The current version supports 4 different look and feel for toast.

Info, Error, Warning, Success

from uiwiz import ui, UiwizApp
app = UiwizApp()

@app.ui("/click/button")
def click_button():
    ui.toast("Clicked")
    ui.toast("Clicked").info()
    ui.toast("Clicked").success()
    ui.toast("Clicked").warning()
    ui.toast("Clicked").error()

ui.button("Click me").on_click(click_button, swap="none")
Want to have element inside the toast?
from uiwiz import ui, UiwizApp
app = UiwizApp()

@app.ui("/click/button/rich")
def click_button_rich():
    with ui.toast():
        ui.html("This is the github logo")
        svg = ui.html('''
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
    <path
        d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
</svg>
''')
        # Style the gh logo to match the current theme
        svg.attributes["style"] = "fill: oklch(var(--bc)/1);"
        svg.classes("rounded-full")

ui.button("Click me").on_click(click_button_rich, swap="none")

Components

To create a component just create a method with the ui elements
def component(data: dict):
    with ui.element():
        ui.label("This is a component")
        for key, value in data.items():
            ui.html(f"Key: {key}, Value: {value}")

component({"key1": "value1", "key2": "value2"})
Key: key1, Value: value1
Key: key2, Value: value2

Show

The show method can take different types and show the data
data = {
    "name": "Karl",
    "age": 20,
    "city": "Random"
}
with row():
    ui.show(data)
    ui.show([data])
name
Karl
age
20
city
Random
nameagecity
Karl20Random
Karl20Random
Karl20Random

Drawer

Create a drawer for navigation links
with ui.drawer(always_open=True, right=False) as drawer:
    with drawer.drawer_content():
        with ui.nav().classes("w-full navbar"):
            with ui.label(for_=drawer.drawer_toggle).classes("btn drawer-button lg:hidden"):
                ui.html(get_svg("menu"))

        ui.label("test1")
        with ui.footer():
            ui.label("some footer text")

    with drawer.drawer_side():
        with ui.element("li"):
            ui.link("Github", "https://github.com/Declow/uiwiz")