Components menu
serez-ui · Component catalog

Components

Every serez-ui built-in component, explained the way you will use it: what it is for, all of its props with types and defaults, its keyboard behavior, and a working example. Use the sidebar to jump to any component — they are grouped by type.

The pattern behind every component. serez-ui components are controlled: the component never keeps its own state — you pass the current value in a prop and receive the next value in a callback (onChange, onToggle, onDrop…). Assigning that value to a field of this triggers a re-render, and the UI reflects the new state. All components render in the GUI and in the TUI from the same JSX; focus and keyboard navigation are built in (Tab moves, Enter/Space activates). Colors, spacing and fonts come from .szs.

Actions

Button

The basic action trigger. Its text is the children, so anything dynamic can go inside the tag. Use it for every "do something now" interaction — submit, add, delete, open. A disabled Button is greyed out, ignores clicks and is skipped by Tab.

PropTypeDefaultDescription
onClickfn()Called on click, or on Enter/Space while focused
disabledboolfalseGrey out, ignore clicks and skip in the focus order
<Button onClick={save} disabled={!this.canSave}>Save</Button>
<Button onClick={() => { this.count = this.count + 1 }}>Clicked {this.count} times</Button>

Style it from .szs with Button { background-color: …; color: …; border-radius: …; }.

An underlined, accent-colored action for navigation-flavored clicks — open a URL, jump to another view, reveal something. Visually lighter than a Button; same activation rules.

PropTypeDefaultDescription
hrefstringDestination shown/used by the link
onClickfn()Called on click or Enter while focused
disabledboolfalseGrey out and skip in the focus order
<Link href="https://serez.dev" onClick={openDocs}>Open the docs</Link>
<Link onClick={() => { this.view = "settings" }}>Go to settings</Link>

Text input

Input

One-line text field. The caret is fully positionable: click places it under the cursor, dragging selects, Home/End jump, Backspace/Delete auto-repeat while held. onChange fires on every edit with the whole new value — store it back or the field snaps to the old value (that is the controlled pattern working as intended).

PropTypeDefaultDescription
valuestring""Current text — the single source of truth
placeholderstringMuted hint shown while the value is empty
typestringtext"password" masks every character
onChangefn(value)Fires on every edit with the full new text
onSubmitfn()Fires when Enter is pressed in the field
disabledboolfalseRead-only look, no focus, no edits
<Input value={this.name} placeholder="your name" onChange={(v) => { this.name = v }} />
<Input value={this.pass} type="password" placeholder="password"
       onChange={(v) => { this.pass = v }} onSubmit={() => { this.login() }} />

Textarea

Multi-line editor with caret and its own vertical scrollbar (mouse wheel works inside). Enter inserts a newline — submit with a Button next to it. rows fixes the visible height; longer content scrolls.

PropTypeDefaultDescription
valuestring""Current text (may contain newlines)
placeholderstringMuted hint shown while empty
rowsint6Visible height in text lines
onChangefn(value)Fires on every edit with the full new text
disabledboolfalseRead-only look, no focus, no edits
<Textarea value={this.bio} rows={4} placeholder="about you"
          onChange={(v) => { this.bio = v }} />

Selection

Checkbox

A boolean box with a label. Click it or press Space while focused; onChange receives the new state. The label can be passed as a prop or as the children — both work.

PropTypeDefaultDescription
checkedboolfalseCurrent state
labelstringchildren textText next to the box
onChangefn(bool)Fires with the new state on toggle
disabledboolfalseGrey out and skip in the focus order
<Checkbox checked={this.agreed} label="I agree" onChange={(b) => { this.agreed = b }} />

Switch / Toggle

On/off pill switch — exactly the same props and semantics as Checkbox, different look: reach for it for settings ("dark mode", "notifications") and keep Checkbox for confirmations and lists. Toggle is an alias for the same component.

PropTypeDefaultDescription
checkedboolfalseCurrent state
labelstringchildren textText next to the pill
onChangefn(bool)Fires with the new state on toggle
disabledboolfalseGrey out and skip in the focus order
<Switch checked={this.dark} label="Dark mode" onChange={(b) => { this.dark = b }} />

RadioGroup

One choice among a few always-visible options (dots). Click an option or move the selection with /. Prefer it over Select/Dropdown when there are 2–5 options and seeing all of them at once helps the user decide.

PropTypeDefaultDescription
valuestringThe currently selected option
options[string]The choices, one dot per entry
onChangefn(value)Fires with the picked option
disabledboolfalseGrey out and skip in the focus order
<RadioGroup value={this.size} options={["S", "M", "L"]} onChange={(v) => { this.size = v }} />

Select

The compact picker: shows one value and cycles through the options on click or /. No overlay — good for short lists (2–4 values) and tight layouts. For longer lists where the user should see everything, use Dropdown.

PropTypeDefaultDescription
valuestringThe currently selected option
options[string]The values it cycles through, in order
onChangefn(value)Fires with the next option on each cycle
disabledboolfalseGrey out and skip in the focus order
<Select value={this.mode} options={["fast", "normal", "slow"]} onChange={(v) => { this.mode = v }} />

A real drop list with an overlay: click or Enter opens it over the widgets below, / move through the options, Enter picks and closes. The overlay draws above everything (z-order handled for you).

PropTypeDefaultDescription
valuestringThe currently selected option
options[string]The list shown when open
onChangefn(value)Fires with the picked option
disabledboolfalseGrey out and skip in the focus order
<Dropdown value={this.lang} options={["es", "en", "fr"]} onChange={(v) => { this.lang = v }} />

Slider

Numeric picker over a horizontal track. Click anywhere on the track to jump, drag the knob for continuous change, or nudge by step with /. The value is clamped to [min, max].

PropTypeDefaultDescription
valueintminCurrent value
minint0Lower bound
maxint100Upper bound
stepint1Keyboard/quantization increment
onChangefn(value)Fires with the new value while it changes
disabledboolfalseGrey out and skip in the focus order
<Slider value={this.vol} min={0} max={100} step={5} onChange={(v) => { this.vol = v }} />
<Label>Volume: {this.vol}%</Label>

Structure & navigation

Row / Col

The layout pair. Row places its children side by side — each at its content width, with a gap — and stacks them vertically when they no longer fit (that is the responsive autosize; force a single line with wrap={false}). Col stacks children vertically. Tune spacing from .szs (gap, padding, direction: column).

PropTypeDefaultDescription
wrapbooltrueRow only — stack vertically when children no longer fit
<Row>
    <Button onClick={save}>Save</Button>
    <Button onClick={clear}>Clear</Button>
</Row>
<Col>
    <h2>Stacked</h2>
    <p>One under the other.</p>
</Col>

Tabs

Controlled tab strip: it draws the tabs (active one underlined) and you render the panel for the active index — the component does not hold or switch content by itself. Click a tab or switch with / while focused.

PropTypeDefaultDescription
tabs[string]One tab per entry, in order
activeint0Index of the selected tab
onChangefn(index)Fires with the clicked/keyed tab index
disabledboolfalseGrey out and skip in the focus order
<Tabs tabs={["Info", "Config", "Logs"]} active={this.tab} onChange={(i) => { this.tab = i }} />
{ this.tab == 0 ? <Info /> : this.tab == 1 ? <Config /> : <Logs /> }

Collapsible / Accordion

A header with a chevron that shows or hides its children. Click the header or press Enter/Space while focused. Stack several (each with its own open flag) to build an accordion — close the others in the onToggle if you want only one open at a time. Accordion is an alias.

PropTypeDefaultDescription
titlestringHeader text next to the chevron
openboolfalseWhether the children are visible
onTogglefn(bool)Fires with the new open state
disabledboolfalseGrey out and skip in the focus order
<Collapsible title="Advanced" open={this.adv} onToggle={(o) => { this.adv = o }}>
    <Checkbox checked={this.verbose} label="Verbose logs" onChange={(b) => { this.verbose = b }} />
    <Slider value={this.retries} min={0} max={10} onChange={(v) => { this.retries = v }} />
</Collapsible>

Files

Both components hand you paths. Declare the File permission in serez.json to read the contents of what the user picked or dropped.

FileInput

A "choose file" button that opens the native OS file dialog (also with Enter/Space while focused) and then shows the picked file name next to the button. With save={true} it opens a save dialog instead — combine with defaultName for the suggested file name.

PropTypeDefaultDescription
onChangefn(path)Fires with the absolute path of the picked file
valuestring""Currently picked path (controls the shown name)
labelstringElegir archivo...The button text
placeholderstringNingun archivoText shown while nothing is picked
filterNamestringHuman name of the dialog filter (e.g. "JSON")
extsstringComma-separated extensions the dialog filters ("png,jpg")
saveboolfalseOpen a save dialog instead of an open dialog
defaultNamestringSuggested file name in the save dialog
<FileInput exts="json" filterName="JSON" value={this.path}
           onChange={(p) => { this.path = p }} />
<FileInput save={true} defaultName="report.json" label="Export…"
           onChange={(p) => { this.exportTo(p) }} />

DropZone

A drag-and-drop target for OS files: it highlights while files hover over the window and onDrop receives every dropped path at once. For drops anywhere in the window (not just over a zone), override the onFilesDropped(paths) hook on your Window instead.

PropTypeDefaultDescription
onDropfn(paths)Fires with the array of dropped absolute paths
labelstringArrastra archivos aquiText shown inside the zone
heightint120Height of the drop area in px
<DropZone label="Drop images here" height={140} onDrop={(paths) => { this.images = paths }} />

Presentation & data

Label

Muted caption text — the children are the content. Non-interactive (skipped by focus). For headings and body text use the HTML-like tags (h1h3, p, span); Label is for the small print next to controls.

<Label>Press Save when you are done</Label>

Image

Raster image (PNG/JPG) from a file path — or from bytes in memory via bytes, e.g. an image you just downloaded with fetch. Give one or both dimensions; with only one, the aspect ratio is preserved.

PropTypeDefaultDescription
srcstringPath to a PNG/JPG on disk
bytesbytesIn-memory image data (alternative to src)
widthintnaturalTarget width in px
heightintnaturalTarget height in px
alphaintopaqueBlend the image (lower = more transparent)
altstringText shown if the image cannot load
<Image src="assets/logo.png" width={64} alt="logo" />
<Image bytes={this.png} height={120} alt="fetched preview" />

ProgressBar

Shows progress from value toward max, with an optional caption. Non-interactive. For a live bar during long work, update the value from the onFrame() hook and return true to redraw.

PropTypeDefaultDescription
valueint0Current progress
maxint100The value that means 100%
labelstringCaption drawn with the bar
<ProgressBar value={this.done} max={this.total} label="downloading" />

Table

Read-only grid: a header row from columns and aligned cells from rows — an array of rows, each an array of cell strings. Good for response headers, metrics, key/value listings. It does not sort or select; render your own controls around it for that.

PropTypeDefaultDescription
columns[string][]Header cells, one per column
rows[[string]][]The data — one inner array per row
<Table columns={["Name", "Qty", "Price"]}
       rows={[["Apples", "4", "$2.00"], ["Pears", "2", "$1.50"]]} />

Chart

Plots a numeric series with the core's vector primitives (antialiased lines and fills). Three shapes: line, area (filled) and bar. By default the scale fits the data — pin it with min/max when comparing charts or animating. In the TUI it degrades to a sparkline. Non-interactive.

PropTypeDefaultDescription
data[number][]The series to plot, in order
typestringlineline | area | bar
heightint160Height of the plot in px
colorstring.szs accentSeries color (also settable via .szs background-color)
minnumberautoPin the lower bound of the scale
maxnumberautoPin the upper bound of the scale
dotsboolfalseMark each data point
<Chart data={this.latencies} type="area" height={140} dots={true} />
<Chart data={[3, 7, 4, 9]} type="bar" min={0} max={10} color="#22c55e" />

Overlays & feedback

Tooltip

Wraps a single child and shows a small box with the tip next to the cursor while it hovers over the child. Purely presentational — the child keeps its own behavior and focus.

PropTypeDefaultDescription
tipstringThe text shown on hover
<Tooltip tip="Sends the request"><Button onClick={send}>Send</Button></Tooltip>

When open is true, it dims the whole window behind a translucent scrim and centers a box with the title and your children on top, capturing clicks and focus. Closing is state like everything else: the header's × button fires onClose, and your own buttons just set the flag back to false.

PropTypeDefaultDescription
openboolfalseShow or hide the modal
titlestring""Header text of the centered box
onClosefn()Fires when the user clicks the × close button
<Modal open={this.confirm} title="Delete item?" onClose={() => { this.confirm = false }}>
    <p>This cannot be undone.</p>
    <Row>
        <Button onClick={doDelete}>Delete</Button>
        <Button onClick={() => { this.confirm = false }}>Cancel</Button>
    </Row>
</Modal>

Toast

Transient feedback banner. kind picks the color: neutral info, green success, yellow warn, red error. Render it conditionally; for the auto-dismiss, count down in the per-frame onFrame() hook and clear the message:

PropTypeDefaultDescription
messagestringchildren textThe banner text
kindstringinfoinfo | success | warn | error
{ this.toast != "" ? <Toast message={this.toast} kind="success" /> : null }
// show it…
this.toast = "Saved!"
this.toastTicks = 120          // ~2 s at 60 fps

// …and auto-dismiss from the per-frame hook of your Window
public onFrame() {
    if (this.toastTicks > 0) {
        this.toastTicks = this.toastTicks - 1
        if (this.toastTicks == 0) { this.toast = ""; return true }
    }
    return false
}

See also

  • serez-ui reference— the component model, focus & keyboard, hooks, panels and the native renderer.
  • .szs styling — colors, fonts, spacing, reactive conditions and media queries for everything above.
  • Build a UI guide — a task-list app built step by step, TUI and GUI.