Skip to main content

pyweft-ui

Ready-made controls for PyWeft: buttons, menus, chips, sliders, form fields with validation, autocomplete and input masks, date and date-range pickers, an event calendar, cards, layout, tabs, tables, dialogs, toasts and more, styled and accessible out of the box. Build a good-looking app in Python without writing HTML or CSS.

<template>
  <ui.Theme>
    <ui.AppShell title="Orders">
      <ui.Card title="New order">
        <ui.Column>
          <ui.TextField label="Customer" @bind="customer" required="true" />
          <ui.Select label="Size" options="Small, Medium, Large" @bind="size" />
          <ui.Switch label="Express delivery" @bind="express" />
        </ui.Column>
        <Fill name="footer">
          <ui.Button variant="ghost" @click="reset">Reset</ui.Button>
          <ui.Button variant="primary" icon="send" :loading="saving" @click="submit">
            Place order
          </ui.Button>
        </Fill>
      </ui.Card>
    </ui.AppShell>
  </ui.Theme>
</template>

<code>
from pyweft_ui import ui

customer: str = ""
size: str = "Medium"
express: bool = False
saving: bool = False

def reset(self):
    self.customer, self.size, self.express = "", "Medium", False

async def submit(self):
    ...
</code>

New to the toolkit? Read the guide: page layout, forms with validation, dialogs, themes and building your own components, with complete examples. This README is the compact reference.

Install

Install it into the same environment as PyWeft itself (see PyWeft's quick start):

uv add pyweft-ui

With pip:

pip install pyweft-ui

Then add "pyweft-ui" to the dependencies list in your app's own pyproject.toml (myapp/pyproject.toml), import it in a component's code block with from pyweft_ui import ui, and use the controls as <ui.Name>. pyweft run and pyweft build see that it is installed locally and send it to the browser for you.

Controls

Every control is a PyWeft component: attributes set its fields, @bind connects it to a field of yours, and attributes it doesn't have, such as class, style, id and aria-*, go onto its outer element. Plain attribute values are converted to each field's type, so cols="3" is the number 3 and disabled="true" is True.

Layout

Control Fields Notes
Theme mode (light, dark, auto), primary, radius, font Wrap your app in it to set colours and corners for everything inside. Optional.
AppShell title, width (narrow, normal, wide, full), sticky_nav Header, optional sidebar and content; the sidebar stays in view while the page scrolls. Fills: brand, actions, nav.
Row gap, align, justify (start, center, end, between), wrap Children side by side.
Column gap, align Children stacked.
Grid cols, min_width, gap Fixed columns, or as many as fit.
Card title, subtitle Fills: title, actions, footer.
Carousel items, label, per_view, gap, loop, autoplay (seconds), arrows, indicators (dots, none) Slides moved by arrows, dots, swipe or drag; @bind to the first shown slide's index; emits change. Fill: slide (with slide, index, active).
Divider label, align (center, start, end), vertical, inset ("true" or a length), spacing (none, sm, md, lg) A thin line; with label, text on the line; vertical between items in a Row.
Expansion title, description, icon, open, disabled, toggle (after, before, none), level, lazy @bind to a bool; emits open, close. Fills: header, actions.
Accordion options (dicts may add description, icon, disabled), multiple, disabled, toggle, level, lazy @bind to the open panel's value (multiple: a list). The content between the tags is each panel's body; panel inside it is that panel's value. Up/Down, Home/End move between headers.

Gaps take none, xs, sm, md, lg, xl, or any CSS length.

Form fields

All have label, hint, error, required, disabled, name, and take @bind.

Control Binds Extra fields
TextField str type (text, email, password, search, url, tel, date, time), placeholder, autocomplete, mask ("(999) 999-9999": 9 digit, a letter, * either), readonly; emits enter on Enter
TextArea str rows, placeholder, readonly
NumberField int or float min, max, step, placeholder, readonly
Select the chosen option's value options, placeholder
RadioGroup the chosen option's value options, inline
Checkbox bool
Switch bool
Autocomplete the chosen option's value (free="true": the typed text) options, match (contains, starts, none), max_results, min_chars, free, loading, empty, placeholder, align; emits search (the text, each keystroke), select (the whole option), enter. Fills: option (with option, active, query), empty
CascadeSelect the chosen option's value nested options (value, label, children; children: None loads on demand via load), any_level, show_path, separator, clearable, placeholder, name; emits select (the path), load, blur. Fill: option (with option, chosen)
ButtonToggle the chosen value; multiple="true": a list options (dicts may add icon, icon_only, disabled), multiple, allow_none, check, size (sm, md, lg), full_width, orientation, aria_label, name
Chips the chosen value; multiple="true": a list options (dicts may add icon, disabled), multiple, required, removable (emits remove), selectable. Fill: option (with option, selected)
ChipInput a list options (suggestions), free, separators, max, add_on_blur, match, placeholder, name; emits add, remove. Fills: chip (with chip), option
Slider an int or float; range="true": (low, high) min, max, step, range, value_label (drag/always/side/none), format, ticks, marks, lazy, name; emits change, blur
DateRangePicker (start, end) dates or None min, max, min_days, max_days, months, placeholder, format, week_start, align; name sends <name>_start and <name>_end
Calendar datetime.date, or with range="true" (start, end) always open on the page: range, months, min, max, min_days, max_days, week_start
DatePicker datetime.date or None min, max (a date or "YYYY-MM-DD"), placeholder, format (strftime), week_start (monday, sunday), align

options can be a comma-separated string ("Small, Medium, Large"), a list of values, a list of (value, label) pairs or a list of {"value": ..., "label": ...} dicts. The bound field gets the value itself, so [(1, "One"), (2, "Two")] binds an int.

TextField, TextArea, NumberField, Select and DatePicker emit blur when the user leaves them, which is when a form starts showing that field's message.

Data and navigation

Control Fields Notes
Tabs options, label, variant (line, pills) @bind to the chosen option's value. The content between the tags is the panel; tab inside it is the chosen value. Arrow keys, Home and End move between tabs.
Stepper steps (dicts may add description, optional, error, done, disabled), linear, orientation (horizontal, vertical), label_position (end, bottom), label, back_label, next_label, finish_label @bind to the current step's value; the content between the tags is its body (step, index). @next runs before moving on (returning False stays); emits finish, back. Fill: actions (with go_next(), go_back(), first, last).
Tree nodes (dicts with value, label, icon, children (None: load on open), disabled), checkable, expanded, expand_all, filter, lines, label, empty, no_matches @bind to the selected node's value (checkable: the list of checked leaves). Emits activate, toggle, load. Fill: node (with node, level, expanded, selected). Arrow keys, Home/End, *, type-ahead.
Breadcrumbs items, auto, home, home_icon, separator (chevron, slash, text), max, label The trail to the current page; links go through the router. auto="true": built from the URL and each page's crumb. With @select, items are buttons. Fill: item (with item, current).
EventCalendar events, view (month, week, agenda), months, date, week_start, day_start, day_end, max_per_day, time_format, switcher, label Events (dicts or objects with title, start, end, color) by month, week or day; a date start is all day. Emits select (your event), day (the date or hour clicked), navigate ((first, last) showing).
Table rows, columns, row_key, sortable, sort, striped, dense, caption, empty rows are dicts or objects. Emits select with the clicked row. Fills: cell (with row, column, value, text), empty.

Table columns are field names ("id, customer"), (field, heading) pairs, or dicts with key and optionally label, format (",.2f" or a function), align and sortable. Left out, they come from the first row. sort="-total" starts sorted by total, largest first.

Actions and feedback

Control Fields Notes
Button variant (primary, secondary, ghost, danger), size (sm, md, lg), icon, loading, disabled, block, type Emits click.
Menu label, items, icon, icon_only, variant, align (start, end), position (below, above), caret, disabled A button that opens a list of actions; emits select with the chosen item's value. items: text, (value, label) pairs, or dicts with value, label, icon, description, hint, danger, disabled and items (a submenu); "-" is a divider, {"heading": ...} a group heading. Fills: trigger (your own button content), item (with item).
ContextMenu items, label, disabled Wraps content; right-click (or the Menu key, Shift+F10) opens items at the pointer. Emits select, open. Fill: item.
Dialog title, width, dismissible @bind to a bool: open while it is True. Esc, a click outside or the X close it. Keeps Tab inside while open and gives focus back when closed. Fill: footer.
BottomSheet title, items (dicts may add icon, description, danger, disabled), max_width, dismissible, aria_label @bind to a bool. Slides up from the bottom; choosing an item emits select and closes it. Esc, a tap outside or a swipe down close it; emits close with how. Fill: actions.
Popover label, icon, icon_only, variant (primary, secondary, ghost, link), caret, title, open_on (click, hover), position (below, above, start, end), align (start, center, end), arrow, width, disabled A button opening a panel of your content beside it. Click mode: @bind a bool, emits open/close; click outside or Esc closes. Hover mode: a preview card shown by CSS. Fill: trigger.
Tooltip text, position (above, below, start, end), tip_id, disabled Wraps a control; shows text on hover or keyboard focus (CSS only). Pair tip_id with the control's aria-describedby.
Toaster look (toast, snackbar), position (bottom-right, bottom-left, bottom-center, top-left, top-center, top-right), limit, dismissible Put one on the page; it shows what toast() raises. look="snackbar": a compact dark bar, bottom centre, one at a time.
Alert kind (info, success, warning, danger), title, dismissible Emits close.
Badge kind (neutral, primary, info, success, warning, danger)
Spinner size, label
Progress value (none: indeterminate), max, buffer, label, aria_label, show_value, format, shape (bar, circle), kind, size (sm, md, lg), striped How far along something is; a bar, or a ring with the value inside.
Icon name, size check, x, plus, minus, search, info, alert, chevron-up/down/left/right, menu, trash, edit, send, user, home, star, heart, download, upload, external, copy, settings, sun, moon, calendar, more, bold, italic, underline, align-left/center/right, list, grid, folder, folder-open, file

Checking forms

from pyweft_ui import Form, rules

form = Form(
    email=[rules.required(), rules.email()],
    password=[rules.required(), rules.min_length(8)],
    agreed=rules.accepted("Agree to the terms to continue"),
)

async def submit(self):
    if not self.form.check():     # shows every message, focuses the first field to fix
        return
    ...
<ui.TextField ref="email" label="Email" @bind="email"
              :error="form.error('email')" @blur="form.touch('email')" />

A field's message shows once the user has left it or tried to submit, then follows what they type. Rules: required, accepted, email, url, min_length, max_length, min_value, max_value, pattern, one_of, same_as and check, or any function of the value that returns a message. form.valid, form.errors and form.reset() do what they say. The guide has the details.

Toasts

from pyweft_ui import toast, clear_toasts

toast("Saved")                                   # info, closes after 4 s
toast("Payment failed", "danger", title="Oops")  # kind: info, success, warning, danger
toast("Archived", action="Undo", on_action=undo) # a button that calls undo()
note = toast("Uploading…", duration=None)        # stays until note.close()

Call it from any handler or task; the page's <ui.Toaster /> shows it.

Theming

<ui.Theme mode="auto" primary="#0f766e" radius="12px">
  ...
</ui.Theme>

Every colour is a CSS variable (--ui-primary, --ui-text, --ui-surface, --ui-border, --ui-danger ...), so you can also set them in your own CSS.

gallery/ is a PyWeft app showing every control. From the PyWeft repository:

uv run pyweft run packages/pyweft-ui/gallery

Metadata

Release files for pyweft-ui 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyweft-ui 0.4.0
File Size Uploaded Provenance Actions
pyweft_ui-0.4.0.tar.gz 174.9 kB With provenance Details for pyweft_ui-0.4.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for pyweft-ui 0.4.0
File Interpreter ABI Platform Actions
pyweft_ui-0.4.0-py3-none-any.whl Python 3 none any Details for pyweft_ui-0.4.0-py3-none-any.whl

Total release size: 297.4 kB

Release files / pyweft_ui-0.4.0.tar.gz

Download URL pyweft_ui-0.4.0.tar.gz
Size 174.9 kB
Tags Source
SHA-256 checksum
How to use checksums
3a63e15fd7c2733b015be49d8e618e7c8ebe8e18837a0be6d15f748e909d3b16
BLAKE2b-256 checksum
How to use checksums
f69080ddf7b4952837b1ee6456d8cc9cbc290084f47b87b5166595a13aedb148
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.

Transparency log

Release files / pyweft_ui-0.4.0-py3-none-any.whl

Download URL pyweft_ui-0.4.0-py3-none-any.whl
Size 122.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
507a07a74364993056c8071a0740815181fdeb113fa7af11fbf5c0131e634821
BLAKE2b-256 checksum
How to use checksums
2be1f94e21bc8ccf9a4f260391d97165c9efbefd4b982da349c57e325519d745
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.

Transparency log

Security for pyweft-ui 0.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for pyweft_ui-0.4.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page