More interface elements
Grids, stats, timers, markdown, menus, kanban boards, ratings and the other building blocks for rich pages.
Functions
ui.grid(props)
Children in a grid: columns (1 to 6, 2 by default) or min_width (as many as fit), gap.
props:{ columns?: integer, min_width?: integer, gap?: integer, [integer]: table }
Returns UiNode
ui.grid{ columns = 3, ui.stat("Viewers", 120), ui.stat("Likes", 48), ui.stat("Tips", "$12") }ui.center(props)
Its children in the middle, across and down.
props:table
Returns UiNode
ui.split(props)
Two children side by side (ratio of the width for the first, 0.2 to 0.8), stacked when narrow.
props:{ ratio?: number, [1]: table, [2]: table }
Returns UiNode
ui.collapse(props)
A section that opens and closes: { title, open, on_toggle(ev) (ev.value is open), …children }.
props:{ title?: string, open?: boolean, on_toggle?: fun(ev: UiEvent), [integer]: table }
Returns UiNode
ui.group(props)
Children under a small title, without a box: { title, description, …children }.
props:{ title?: string, description?: string, [integer]: table }
Returns UiNode
ui.toolbar(props)
A row spread across the width that wraps: actions on one side, filters on the other.
props:table
Returns UiNode
ui.stat(label, value, props)
A number that matters: ui.stat("Viewers", 120, { delta = "+12", trend = "up", hint = "last hour" }).
Name
Type
Returns UiNode
ui.icon(name, props)
An icon by name ("heart", "trophy", "music", "users"…; the common lucide icons): ui.icon("heart", { size = 20, color = "#ff4fa3" }).
name:string|tableprops:{ size?: integer, color?: string }, optional
Returns UiNode
ui.avatar(props)
A round picture (https) or someone's initials: { src, name, size }.
props:{ src?: string, name?: string, size?: integer }
Returns UiNode
ui.kbd(text)
A key cap: ui.kbd("Ctrl+K").
text:string|table
Returns UiNode
ui.chip(text, props)
A small tag: ui.chip("New", { tone = "accent", on_remove = fn }) (× when on_remove).
text:string|tableprops:{ tone?: string, on_remove?: fun(ev: UiEvent) }, optional
Returns UiNode
ui.status(props)
A coloured dot and a few words: { text, tone = "success"|"warning"|"error"|"info"|"neutral", pulse }.
props:{ text?: string, tone?: string, pulse?: boolean }
Returns UiNode
ui.callout(props)
A calm banner: { tone = "info"|"success"|"warning"|"error", title, text, …children }.
props:{ tone?: string, title?: string, text?: string, [integer]: table }
Returns UiNode
ui.empty(props)
What shows when there's nothing yet: { icon, title, text, …buttons }.
props:{ icon?: string, title?: string, text?: string, [integer]: table }
Returns UiNode
ui.spinner(props)
A spinning loader: { size, label }.
props:{ size?: integer, label?: string }, optional
Returns UiNode
ui.skeleton(props)
Grey placeholder lines while something loads: { lines = 3, height }.
props:{ lines?: integer, height?: integer }, optional
Returns UiNode
ui.timeline(props)
Things in order: { items = { { title, text, time, tone } } }.
props:{ items?: { title?: string, text?: string, time?: string, tone?: string }[] }
Returns UiNode
ui.markdown(text)
Formatted text: bold, italic, code, links, lists, headings and quotes.
Nothing else is drawn (no HTML, no pictures).
text:string|table
Returns UiNode
ui.quote(props)
A quote: { text, cite }.
props:{ text?: string, cite?: string }
Returns UiNode
ui.gauge(props)
A round meter: { value = 0 to 1, label, text, tone }.
props:{ value?: number, label?: string, text?: string, tone?: string, size?: integer }
Returns UiNode
ui.sparkline(props)
A small line of numbers: { values = { … }, color, height }.
props:{ values?: number[], color?: string, height?: integer }
Returns UiNode
ui.timer(props)
A clock that ticks by itself (no need to redraw): to counts down to a time, from counts up
from one (ms since 1970); format "auto", "mm:ss" or "hh:mm:ss"; done_text when it reaches 0.
props:{ to?: integer, from?: integer, format?: string, done_text?: string, label?: string, color?: string, size?: "sm"|"md"|"lg" }
Returns UiNode
ui.timer{ to = time.now() + 5 * 60 * 1000, done_text = "Back!" }ui.countdown(props)
The same as ui.timer (a name that reads better for one counting down).
props:{ to?: integer, from?: integer, format?: string, done_text?: string, label?: string, color?: string, size?: "sm"|"md"|"lg" }
Returns UiNode
ui.properties(props)
Labels and values in two columns: { items = { { label, value } } }.
props:{ items?: { label?: string, value?: string|number }[] }
Returns UiNode
ui.steps(props)
Steps with the current one lit: { steps = { "Connect", "Pick", "Go live" }, current = 2 }.
props:{ steps?: string[], current?: integer }
Returns UiNode
ui.number(props)
A number with − and + buttons: { name, label, value, min, max, step, suffix, on_change }.
props:{ name?: string, label?: string, value?: number, min?: number, max?: number, step?: number, suffix?: string, on_change?: fun(ev: UiEvent) }
Returns UiNode
ui.radio(props)
One choice of a few, all shown: { name, label, options = { { value, label, hint } }, value, on_change }.
props:{ name?: string, label?: string, options?: UiOption[], value?: any, direction?: "row"|"column", on_change?: fun(ev: UiEvent) }
Returns UiNode
ui.segmented(props)
One choice of a few, as joined buttons: { name, options = { { value, label, icon } }, value, on_change }.
props:{ name?: string, options?: UiOption[], value?: any, on_change?: fun(ev: UiEvent) }
Returns UiNode
ui.multiselect(props)
Several choices: { name, label, options, value = { … }, on_change } (ev.value is a list).
props:{ name?: string, label?: string, options?: UiOption[], value?: any[], on_change?: fun(ev: UiEvent) }
Returns UiNode
ui.tags(props)
Words the streamer types (Enter adds one): { name, label, value = { … }, placeholder, max, on_change }.
props:{ name?: string, label?: string, value?: string[], placeholder?: string, max?: integer, on_change?: fun(ev: UiEvent) }
Returns UiNode
ui.search(props)
A search box: on_change a moment after typing stops, on_submit on Enter.
props:{ name?: string, placeholder?: string, value?: string, on_change?: fun(ev: UiEvent), on_submit?: fun(ev: UiEvent) }
Returns UiNode
ui.rating(props)
Stars: { name, label, value, max = 5, on_change }.
props:{ name?: string, label?: string, value?: integer, max?: integer, on_change?: fun(ev: UiEvent) }
Returns UiNode
ui.range(props)
A slider with two ends: { name, label, min, max, step, value = { lo, hi }, on_change }.
props:{ name?: string, label?: string, min?: number, max?: number, step?: number, value?: number[], on_change?: fun(ev: UiEvent) }
Returns UiNode
ui.time(props)
A time of day: { name, label, value = "20:30", on_change }.
props:{ name?: string, label?: string, value?: string, on_change?: fun(ev: UiEvent) }
Returns UiNode
ui.hotkey(props)
Records a key combination, in the form hotkeys.on takes ("Ctrl+Shift+KeyB").
props:{ name?: string, label?: string, value?: string, on_change?: fun(ev: UiEvent) }
Returns UiNode
ui.menu(props)
A button with a menu: { label or icon, align, items = { { label, icon, danger, disabled, on_click } | { divider = true } } }
(ev.value is the item's label).
props:{ label?: string, icon?: string, items?: table[] }
Returns UiNode
ui.copy(text, props)
A button that copies text to the clipboard when the streamer clicks it: ui.copy("discord.gg/abc", { label = "Copy invite" }).
text:string|tableprops:{ label?: string }, optional
Returns UiNode
ui.pagination(props)
Page numbers: { page, pages, on_change } (ev.value is the page asked for).
props:{ page?: integer, pages?: integer, on_change?: fun(ev: UiEvent) }
Returns UiNode
ui.tree(props)
Nested items to pick from: { items = { { id, label, icon, children = { … } } }, value, on_change }.
props:{ items?: table[], value?: string, on_change?: fun(ev: UiEvent) }
Returns UiNode
ui.popover(props)
A button that opens a small panel with the children: { label, icon, panel_width, align, on_toggle, …children }. What is typed in it is gone when it closes.
props:{ label?: string, icon?: string, panel_width?: integer, align?: "start"|"center"|"end", on_toggle?: fun(ev: UiEvent), [integer]: table }
Returns UiNode
ui.kanban(props)
Cards in columns the streamer drags between (a song queue, ideas, to do):
{ columns = { { id, title, items = { { id, label, hint } } } }, on_change } (ev.value is the new columns).
props:{ columns?: table[], on_change?: fun(ev: UiEvent) }
Returns UiNode
Types
Open a type to see its fields; type names above link here.

