Closes 5 documented gaps from how-to-add-a-feature.md: - aiui.set_custom_js(path) — inject client JS from Python (new /custom.js route, <script> after plugin-loader). Tests: 10/10 - name= kwarg on all 8 form inputs — stable submit-keys for form_action, avoids label collisions. Tests: 18/18 - Component schema registry — aiui.register_component_schema() + GET /api/components/schemas. Built-in schemas auto-derived from ui.py. Tests: 9/9 - /api/features parallelized via asyncio.gather (warm calls now instant) - mkdocs build fixed for pygments 2.20 via docs_hooks.py Other: - form inputs now emit name + data-label attrs so form_action submit handler collects values correctly (bug fix) - radio submit handler only stores the checked radio's value - switch now has hidden checkbox mirror so form_action sees its state - mkdocs --strict passes - Example 28 uses the new APIs (no more DevTools console workaround) - Bump aiui 0.3.107 -> 0.3.108; sync __version__.py Tests: 542/545 pass (3 pre-existing test-isolation failures unrelated).
27 KiB
Component API Reference
Python functions for building dashboard UIs. Each function returns a dict that the frontend renders as a real UI element — 36 components organized into tiers.
import praisonaiui as aiui
Core Components
aiui.layout(children)
Wrap components in a renderable container.
aiui.layout([
aiui.card("Users", value=42),
aiui.table(headers=["A"], rows=[["x"]]),
])
# Returns: {"_components": [...]}
| Parameter | Type | Required | Description |
|---|---|---|---|
children |
Sequence[dict] |
✅ | List of component dicts |
Returns: dict with _components key — triggers structured rendering in the frontend.
aiui.card(title, *, value, footer)
A metric/stat card.
aiui.card("Revenue", value="$1,500", footer="+8% this month")
| Parameter | Type | Required | Description |
|---|---|---|---|
title |
str |
✅ | Card header label |
value |
Any |
❌ | Main display value |
footer |
str |
❌ | Small text below the value |
aiui.columns(children)
Arrange components in a responsive grid row.
aiui.columns([
aiui.card("A", value=1),
aiui.card("B", value=2),
aiui.card("C", value=3),
])
| Parameter | Type | Required | Description |
|---|---|---|---|
children |
Sequence[dict] |
✅ | Component dicts to lay out side-by-side |
aiui.table(*, headers, rows)
A data table.
aiui.table(
headers=["Name", "Role"],
rows=[["Alice", "Engineer"], ["Bob", "Designer"]],
)
| Parameter | Type | Required | Description |
|---|---|---|---|
headers |
Sequence[str] |
✅ | Column header labels |
rows |
Sequence[Sequence[Any]] |
✅ | Row data arrays |
aiui.text(content)
A text block.
aiui.text("Last updated 30 seconds ago.")
| Parameter | Type | Required | Description |
|---|---|---|---|
content |
str |
✅ | Text content |
aiui.chart(title, *, data, chart_type)
A chart.
aiui.chart("Revenue", data=[{"month": "Jan", "value": 100}], chart_type="line")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
title |
str |
✅ | — | Chart title |
data |
Sequence[dict] |
❌ | [] |
Data points |
chart_type |
str |
❌ | "bar" |
bar, line, or pie |
Essential Components
aiui.metric(label, *, value, delta, delta_color)
A metric card with optional delta indicator.
aiui.metric("Requests", value="12.3k", delta="+15%")
aiui.metric("Errors", value=23, delta="+3", delta_color="inverse")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
label |
str |
✅ | — | Metric label |
value |
Any |
✅ | — | Main metric value |
delta |
str |
❌ | None |
Change indicator (e.g. "+5%") |
delta_color |
str |
❌ | "normal" |
"normal", "inverse", or "off" |
aiui.progress_bar(label, *, value, max_value)
A progress bar.
aiui.progress_bar("Upload", value=73, max_value=100)
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
label |
str |
✅ | — | Label above the bar |
value |
int|float |
✅ | — | Current value |
max_value |
int|float |
❌ | 100 |
Maximum value |
aiui.alert(message, *, variant, title)
An alert/notification box.
aiui.alert("Deploy successful!", variant="success", title="Done")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
message |
str |
✅ | — | Alert body text |
variant |
str |
❌ | "info" |
"info", "success", "warning", "error" |
title |
str |
❌ | None |
Optional bold title |
aiui.badge(text, *, variant)
An inline badge/tag.
aiui.badge("New")
aiui.badge("Deprecated", variant="destructive")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
text |
str |
✅ | — | Badge label |
variant |
str |
❌ | "default" |
"default", "secondary", "destructive", "outline" |
aiui.separator()
A horizontal separator line. No parameters.
aiui.tabs(items)
A tabbed container.
aiui.tabs([
{"label": "Overview", "children": [aiui.text("Tab 1 content")]},
{"label": "Details", "children": [aiui.table(headers=["K","V"], rows=[["a","b"]])]},
])
| Parameter | Type | Required | Description |
|---|---|---|---|
items |
Sequence[dict] |
✅ | List of {"label": str, "children": [comp_dicts]} |
aiui.accordion(items)
A collapsible accordion.
aiui.accordion([
{"title": "FAQ 1", "content": "Answer 1"},
{"title": "FAQ 2", "content": "Answer 2"},
])
| Parameter | Type | Required | Description |
|---|---|---|---|
items |
Sequence[dict] |
✅ | List of {"title": str, "content": str_or_comp} |
aiui.image_display(src, *, alt, caption, width)
An image with optional caption.
aiui.image_display("https://example.com/logo.png", alt="Logo", caption="Our logo", width="300px")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
src |
str |
✅ | — | Image URL or path |
alt |
str |
❌ | "" |
Alt text |
caption |
str |
❌ | None |
Caption below image |
width |
str |
❌ | None |
CSS width (e.g. "300px", "50%") |
aiui.code_block(code, *, language)
A code block with syntax highlighting.
aiui.code_block("def hello():\n print('Hi!')", language="python")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
code |
str |
✅ | — | Source code text |
language |
str |
❌ | "text" |
Language for highlighting |
aiui.json_view(data)
A formatted JSON viewer.
aiui.json_view({"status": "ok", "items": [1, 2, 3]})
| Parameter | Type | Required | Description |
|---|---|---|---|
data |
Any |
✅ | Any JSON-serializable data |
Form Input Components
aiui.text_input(label, *, value, placeholder)
aiui.text_input("Name", value="Alice", placeholder="Enter name")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
label |
str |
✅ | — | Input label |
value |
str |
❌ | "" |
Default value |
placeholder |
str |
❌ | "" |
Placeholder text |
aiui.number_input(label, *, value, min_val, max_val, step)
aiui.number_input("Age", value=28, min_val=0, max_val=150, step=1)
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
label |
str |
✅ | — | Input label |
value |
int|float |
❌ | 0 |
Default value |
min_val |
int|float |
❌ | None |
Minimum allowed |
max_val |
int|float |
❌ | None |
Maximum allowed |
step |
int|float |
❌ | 1 |
Increment step |
aiui.select_input(label, *, options, value)
aiui.select_input("Country", options=["USA", "UK", "Canada"], value="USA")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
label |
str |
✅ | — | Input label |
options |
Sequence[str] |
✅ | — | Option strings |
value |
str |
❌ | "" |
Default selected |
aiui.slider_input(label, *, value, min_val, max_val, step)
aiui.slider_input("Volume", value=65, min_val=0, max_val=100, step=5)
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
label |
str |
✅ | — | Input label |
value |
int|float |
❌ | 0 |
Default value |
min_val |
int|float |
❌ | 0 |
Minimum |
max_val |
int|float |
❌ | 100 |
Maximum |
step |
int|float |
❌ | 1 |
Increment step |
aiui.checkbox_input(label, *, checked)
aiui.checkbox_input("I agree to the terms", checked=True)
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
label |
str |
✅ | — | Checkbox label |
checked |
bool |
❌ | False |
Default state |
aiui.switch_input(label, *, checked)
aiui.switch_input("Enable notifications", checked=False)
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
label |
str |
✅ | — | Switch label |
checked |
bool |
❌ | False |
Default state |
aiui.radio_input(label, *, options, value)
aiui.radio_input("Language", options=["Python", "JavaScript", "Go"], value="Python")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
label |
str |
✅ | — | Group label |
options |
Sequence[str] |
✅ | — | Option strings |
value |
str |
❌ | "" |
Default selected |
aiui.textarea_input(label, *, value, placeholder, rows)
aiui.textarea_input("Bio", value="Developer", placeholder="About you...", rows=3)
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
label |
str |
✅ | — | Input label |
value |
str |
❌ | "" |
Default value |
placeholder |
str |
❌ | "" |
Placeholder text |
rows |
int |
❌ | 4 |
Visible rows |
aiui.multiselect_input(label, *, options, value)
aiui.multiselect_input("Tags", options=["AI", "ML", "NLP"], value=["AI"])
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
label |
str |
✅ | — | Input label |
options |
Sequence[str] |
✅ | — | Available choices |
value |
Sequence[str] |
❌ | () |
Default selections |
aiui.date_input(label, *, value)
aiui.date_input("Start Date", value="2026-01-15")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
label |
str |
✅ | — | Input label |
value |
str |
❌ | None |
ISO date string |
aiui.time_input(label, *, value)
aiui.time_input("Alarm", value="08:30")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
label |
str |
✅ | — | Input label |
value |
str |
❌ | None |
"HH:MM" string |
aiui.color_picker_input(label, *, value)
aiui.color_picker_input("Brand Color", value="#6366f1")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
label |
str |
✅ | — | Input label |
value |
str |
❌ | "#000000" |
Default hex color |
Layout & Advanced Components
aiui.container(children, *, title)
A container wrapper with optional title.
aiui.container([aiui.text("Inside"), aiui.badge("Tag")], title="Section")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
children |
Sequence[dict] |
✅ | — | Child component dicts |
title |
str |
❌ | None |
Optional heading |
aiui.expander(title, *, children, expanded)
A collapsible section.
aiui.expander("Advanced Settings", children=[aiui.text("Hidden content")], expanded=False)
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
title |
str |
✅ | — | Section heading |
children |
Sequence[dict] |
✅ | — | Content components |
expanded |
bool |
❌ | False |
Initially expanded? |
aiui.divider(text)
A horizontal divider with optional center text.
aiui.divider("OR")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
text |
str |
❌ | None |
Label in the middle of the line |
aiui.header(text, *, level)
A heading element.
aiui.header("Dashboard", level=1)
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
text |
str |
✅ | — | Heading text |
level |
int |
❌ | 1 |
Heading level 1–6 |
aiui.markdown_text(content)
A markdown-rendered text block.
aiui.markdown_text("**Bold**, *italic*, and `code`.")
| Parameter | Type | Required | Description |
|---|---|---|---|
content |
str |
✅ | Markdown content string |
aiui.link(text, *, href, external)
A hyperlink.
aiui.link("Docs", href="https://docs.praison.ai", external=True)
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
text |
str |
✅ | — | Link text |
href |
str |
✅ | — | URL |
external |
bool |
❌ | False |
Open in new tab? |
aiui.button_group(buttons)
A row of buttons.
aiui.button_group([
{"label": "Save", "variant": "default"},
{"label": "Cancel", "variant": "outline"},
])
| Parameter | Type | Required | Description |
|---|---|---|---|
buttons |
Sequence[dict] |
✅ | List of {"label": str, "variant": str} |
aiui.stat_group(stats)
A grid of stat/metric cards.
aiui.stat_group([
{"label": "CPU", "value": "72%", "delta": "+5%"},
{"label": "Memory", "value": "4.2 GB"},
])
| Parameter | Type | Required | Description |
|---|---|---|---|
stats |
Sequence[dict] |
✅ | List of {"label": str, "value": Any, "delta": str} |
aiui.avatar(*, src, name, fallback)
An avatar image or initials.
aiui.avatar(name="Alice Smith", fallback="AS")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
src |
str |
❌ | None |
Image URL |
name |
str |
❌ | None |
Display name |
fallback |
str |
❌ | None |
Fallback text (e.g. initials) |
aiui.callout(content, *, variant, title)
A callout/highlight box.
aiui.callout("Use GPU for faster training.", variant="info", title="Tip")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
content |
str |
✅ | — | Callout body text |
variant |
str |
❌ | "info" |
"info", "success", "warning", "error" |
title |
str |
❌ | None |
Optional bold title |
aiui.empty(text)
An empty state placeholder.
aiui.empty("No results found")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
text |
str |
❌ | "No data" |
Placeholder message |
aiui.spinner(text)
A loading spinner with text.
aiui.spinner("Loading data...")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
text |
str |
❌ | "Loading..." |
Loading message |
Media Components
aiui.audio_player(src, *, autoplay)
aiui.audio_player("/audio/clip.mp3", autoplay=False)
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
src |
str |
✅ | — | Audio file URL |
autoplay |
bool |
❌ | False |
Auto-play? |
aiui.video_player(src, *, autoplay, poster)
aiui.video_player("/video/demo.mp4", poster="/img/thumb.jpg")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
src |
str |
✅ | — | Video file URL |
autoplay |
bool |
❌ | False |
Auto-play? |
poster |
str |
❌ | None |
Poster image URL |
aiui.file_download(label, *, href, filename)
aiui.file_download("Download Report", href="/files/report.pdf", filename="report.pdf")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
label |
str |
✅ | — | Button label |
href |
str |
✅ | — | File URL |
filename |
str |
❌ | None |
Suggested filename |
aiui.gallery(items)
An image/media gallery grid.
aiui.gallery([
{"src": "/img/1.jpg", "alt": "Photo 1", "caption": "Sunset"},
{"src": "/img/2.jpg", "alt": "Photo 2"},
])
| Parameter | Type | Required | Description |
|---|---|---|---|
items |
Sequence[dict] |
✅ | List of {"src": str, "alt": str, "caption": str} |
Dashboard Components
aiui.toast(message, *, variant, duration)
A toast notification.
aiui.toast("Saved!", variant="success", duration=3000)
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
message |
str |
✅ | — | Notification text |
variant |
str |
❌ | "info" |
"info", "success", "warning", "error" |
duration |
int |
❌ | 3000 |
Auto-dismiss ms |
aiui.dialog(title, *, children, description)
A modal dialog.
aiui.dialog("Confirm", children=[aiui.text("Are you sure?")], description="This action cannot be undone.")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
title |
str |
✅ | — | Dialog heading |
children |
Sequence[dict] |
✅ | — | Content components |
description |
str |
❌ | None |
Subtitle |
aiui.caption(text)
Small muted caption text.
aiui.caption("Figure 1: System architecture")
| Parameter | Type | Required | Description |
|---|---|---|---|
text |
str |
✅ | Caption content |
aiui.html_embed(content)
Raw HTML embed (trusted content only).
aiui.html_embed('<iframe src="https://example.com" width="100%"></iframe>')
| Parameter | Type | Required | Description |
|---|---|---|---|
content |
str |
✅ | HTML string |
aiui.skeleton(*, width, height, variant)
A skeleton loading placeholder.
aiui.skeleton(width="200px", height="20px", variant="text")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
width |
str |
❌ | None |
CSS width |
height |
str |
❌ | None |
CSS height |
variant |
str |
❌ | "text" |
"text", "card", or "avatar" |
aiui.tooltip_wrap(child, *, content)
Wrap a component with a hover tooltip.
aiui.tooltip_wrap(aiui.badge("Beta"), content="This feature is in beta")
| Parameter | Type | Required | Description |
|---|---|---|---|
child |
dict |
✅ | Component to wrap |
content |
str |
✅ | Tooltip text |
Navigation Components
aiui.breadcrumb(items)
A breadcrumb navigation trail.
aiui.breadcrumb([
{"label": "Home", "href": "/"},
{"label": "Settings", "href": "/settings"},
{"label": "Profile", "href": None},
])
| Parameter | Type | Required | Description |
|---|---|---|---|
items |
Sequence[dict] |
✅ | List of {"label": str, "href": str|None} |
aiui.pagination(*, total, page, per_page)
Pagination controls.
aiui.pagination(total=250, page=3, per_page=25)
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
total |
int |
✅ | — | Total items |
page |
int |
❌ | 1 |
Current page (1-based) |
per_page |
int |
❌ | 10 |
Items per page |
aiui.key_value_list(items, *, title)
A key-value display list.
aiui.key_value_list([
{"label": "Model", "value": "gpt-4o-mini"},
{"label": "Temperature", "value": 0.7},
], title="Config")
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
items |
Sequence[dict] |
✅ | — | List of {"label": str, "value": Any} |
title |
str |
❌ | None |
Optional heading |
aiui.popover(trigger, *, children)
A popover overlay triggered by a component.
aiui.popover(aiui.badge("Info"), children=[aiui.text("More details here.")])
| Parameter | Type | Required | Description |
|---|---|---|---|
trigger |
dict |
✅ | Component that triggers the popover |
children |
Sequence[dict] |
✅ | Content inside the popover |
Composition
Components are designed to compose. Use columns() for horizontal layout within a layout():
@aiui.page("overview", title="Overview", icon="📊")
async def overview():
return aiui.layout([
# Row of KPI cards
aiui.columns([
aiui.metric("Users", value=142, delta="+12%"),
aiui.metric("Revenue", value="$1.5k", delta="+8%"),
aiui.metric("Uptime", value="99.9%"),
]),
# Tabbed data
aiui.tabs([
{"label": "Table", "children": [
aiui.table(
headers=["Agent", "Tasks", "Status"],
rows=[["Researcher", 15, "Active"]],
),
]},
{"label": "Chart", "children": [
aiui.chart("Tasks", data=[{"name": "Researcher", "value": 15}]),
]},
]),
# Footer
aiui.text("Data refreshes every 30 seconds."),
])
Component Count Summary
| Category | Components | Count |
|---|---|---|
| Core | layout, card, columns, table, text, chart |
6 |
| Essential | metric, progress_bar, alert, badge, separator, tabs, accordion, image_display, code_block, json_view |
10 |
| Form Inputs | text_input, number_input, select_input, slider_input, checkbox_input, switch_input, radio_input, textarea_input, multiselect_input, date_input, time_input, color_picker_input |
12 |
| Layout & Advanced | container, expander, divider, header, markdown_text, link, button_group, stat_group, avatar, callout, empty, spinner |
12 |
| Media | audio_player, video_player, file_download, gallery |
4 |
| Dashboard | toast, dialog, caption, html_embed, skeleton, tooltip_wrap |
6 |
| Navigation | breadcrumb, pagination, key_value_list, popover |
4 |
| Total | 54 |
Custom Component Types
The dashboard.js renderer supports any dict with a type key. Unknown types render as formatted JSON, making it safe to experiment:
# Custom component — renders as JSON until frontend support is added
{"type": "timeline", "events": [{"time": "10:00", "label": "Deploy"}]}
Component Render Protocol
Understanding how components flow from Python to the browser.
Data Flow
Python Server Frontend
────── ────── ────────
aiui.card("X", value=42) → {"type":"card",...} → renderComponent()
↓ ↓
@aiui.page() handler → /api/pages/{id}/data → renderCard()
↓ ↓
aiui.layout([...]) → {"_components":[...]} → DOM elements
Step-by-Step
-
Python: Call
aiui.card("Revenue", value="$1,500")— returns a dict:{"type": "card", "title": "Revenue", "value": "$1,500"} -
Server:
@aiui.page()handler returnsaiui.layout([...]):{"_components": [{"type": "card", ...}, {"type": "table", ...}]} -
API:
GET /api/pages/{id}/datareturns the JSON -
Frontend:
dashboard.jsdetects_componentskey and callsrenderComponents():function renderComponents(data, container) { if (data && data._components) { data._components.forEach(comp => { container.appendChild(renderComponent(comp)); }); } } -
Renderer:
renderComponent(comp)dispatches oncomp.type:function renderComponent(comp) { switch (comp.type) { case 'card': return renderCard(comp); case 'table': return renderTable(comp); // ... 48 component types default: return renderAsJSON(comp); } } -
DOM: Each renderer creates HTML elements with
db-*CSS classes
The _components Key
The magic key that triggers structured rendering:
# With _components — renders as UI elements
return {"_components": [aiui.card("X"), aiui.table(...)]}
# Without _components — renders as raw JSON
return {"status": "ok", "data": [...]}
aiui.layout(children) automatically wraps children in {"_components": [...]}.
Component Dict Protocol
Every component function returns a dict with:
| Key | Required | Description |
|---|---|---|
type |
✅ | Component type name (e.g. "card", "table") |
| Other keys | Varies | Component-specific properties |
# Card component
{"type": "card", "title": "Revenue", "value": "$1,500", "footer": "+8%"}
# Table component
{"type": "table", "headers": ["A", "B"], "rows": [["x", "y"]]}
# Nested components
{"type": "columns", "children": [
{"type": "card", "title": "A"},
{"type": "card", "title": "B"},
]}
CSS Classes
All components use db-* prefixed CSS classes:
| Component | CSS Class |
|---|---|
| Card | .db-card, .db-card-title, .db-card-value, .db-card-footer |
| Table | .db-viewer table |
| Alert | .db-alert, .db-alert-info, .db-alert-success, etc. |
| Badge | .db-badge, .db-badge-secondary, .db-badge-destructive |
| Tabs | .db-tabs, .db-tab-list, .db-tab-btn, .db-tab-panel |
Extending with Custom Components
To add a new component type:
-
Python: Add function to
ui.py:def timeline(events: list[dict]) -> dict: return {"type": "timeline", "events": events} -
Frontend: Add renderer to
dashboard.js:function renderTimeline(comp) { const el = document.createElement('div'); el.className = 'db-timeline'; // ... render events return el; } // Add to switch statement in renderComponent() case 'timeline': return renderTimeline(comp); -
CSS: Add styles for
.db-timeline
Until the frontend renderer is added, unknown types render as formatted JSON.