Reference
Every element, directive, event, function and piece of template syntax of xoong, in one place: what it does, what it takes, an example, and the guide that explains it. 175 entries, written from the code itself.
Press Ctrl K (or /) to search everything on this site, or open the Reference menu in the header. The same data is published as reference.json for tools.
What the types mean
text- Any text.
number- A number.
flag- Present means on, absent means off.
list- Comma-separated values.
choice- One of a fixed set; any other value falls back to the default.
path- A path into the data:
items,name.en. operation- An operationId or
METHOD /path. selector- A CSS selector.
JSON- A JSON text.
Elements
Every xo-* element: what it does, its attributes, its events and an example. 47 entries.
<xo-view><xo-table><xo-form><xo-dialog><xo-toast><xo-router><xo-pager><xo-tabs><xo-menu><xo-combobox><xo-tags><xo-richtext><xo-carousel><xo-switch><xo-date><xo-time><xo-slider><xo-stepper><xo-password><xo-copy><xo-image><xo-theme-toggle><xo-accordion><xo-popover><xo-consent><xo-file><xo-command><xo-chart><xo-range-slider><xo-segmented><xo-split-button><xo-textarea><xo-stat><xo-otp><xo-avatar><xo-avatars><xo-progress><xo-spinner><xo-steps><xo-inline-edit><xo-drawer><xo-breadcrumb><xo-alert><xo-skeleton><xo-tree><xo-color><xo-rating>
<xo-view>
Declare a screen: get names the GET operation to read. Children are bound to the result. Add live to apply server-sent updates.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
get | operation | — | A path ("/users") or operationId. |
live | flag | off | Apply live updates from the event stream. |
url-params | list | — | Comma-separated param names kept in the page URL (shareable, survive reload and Back): status,q,sort,offset. |
p-<name> | text | — | The value of the path parameter "name" of the <xo-view get> operation (p-id="42"; p-id="?id" reads ?id= from the page URL; p-id=":id" reads a route parameter). |
Events
xo-doneAn action succeeded.xo-errorA load or an action failed.xo-invalidA form was not sent because it is invalid.xo-noticeA message for the toast.xo-goAn action asked to go to another page.
Example
<xo-view get="listPeople" url-params="name,role">
<p xo-if="$loading">Loading…</p>
<p xo-if="$error">{$error.detail}</p>
<ul>
<li xo-each="items">{name} · {email}</li>
</ul>
</xo-view>
Guide Data and tables
<xo-table>
A table for the list in the enclosing <xo-view get>. Children become the actions cell of each row.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
of | path | items | List field of the response (default items). |
columns | list | — | Comma-separated fields, optionally field:Label. |
sortable | flag | off | Header buttons sort on the server through the sort query parameter. |
selectable | flag | off | A checkbox per row and a select-all box; slot="bulk" children act on the ticked rows. |
filters | list | — | Comma-separated fields that get a search box above the table; typing sets the query parameter of the same name. |
page-size | number | — | Rows per page: sets limit, Previous/Next change offset. The response needs a total. |
cards | flag | off | Draw each row as a card in a narrow pane (a theme does it; the table gets the class xo-cards). Cells carry data-label. |
title | text | the first | The column that is the card title (default: the first). |
status | text | — | The column that is the status badge of the card (data-status). |
empty | text | — | Text shown when the list has no rows. |
page-sizes | list | — | Comma-separated rows-per-page choices: a select beside the pager (needs page-size). |
column-picker | flag | off | A "Columns" dropdown at the right end of the search row: tick a column to show it, untick to hide it (the last visible one stays). Fires xo-columns-change with { visible, hidden }. |
hidden-columns | list | — | With column-picker: comma-separated fields that start hidden. |
columns-key | text | — | With column-picker: a name under which the choice is remembered in this browser (localStorage). |
Events
xo-columns-changeThe person showed or hid a column of the table.
Example
<xo-view get="listPeople">
<xo-table columns="name,email:E-mail,role" sortable filters="name:Search by name" selectable page-size="10" empty="Nobody here">
<span slot="role" class="badge">{role}</span>
<button xo-click="deletePerson" xo-confirm="Delete {name}?" xo-notice="Deleted">Delete</button>
<button slot="bulk" xo-click="deletePerson" xo-selected>Delete ticked</button>
</xo-table>
</xo-view>
Guide Data and tables
<xo-form>
A form generated from the request schema of an operation.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
op | operation | — | The operation to submit to. |
submit | text | — | Text of the submit button. |
Events
xo-doneAn action succeeded.xo-errorA load or an action failed.xo-invalidA form was not sent because it is invalid.xo-dirtyA form gained or lost unsaved changes.xo-revealThe form is about to focus an invalid field.xo-form-readyThe form model exists.
Example
<xo-form op="createPerson" submit="Add person" xo-notice="Person added"></xo-form>
Guide Forms
<xo-dialog>
A modal on the native <dialog>. Open it with xo-open, close it with xo-close or by submitting a form inside. With heading it is a titled modal: a header with the title and a Close button, the content as the body, and the elements marked slot="footer" as the footer.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
heading | text | — | The title: makes it a titled modal (header, body, footer). |
size | choice | 40rem | sm, wide or full (the default is 40rem).One of sm, wide, full. |
dismiss | flag | off | Close it on a click outside the dialog. |
Example
<button xo-open="#add">Add person</button>
<xo-dialog id="add" heading="Add person" size="wide" dismiss>
<xo-form op="createPerson" submit="Add"></xo-form>
<button slot="footer" xo-close>Cancel</button>
</xo-dialog>
Guide Dialogs and modals
<xo-toast>
Shows the message of the latest xo-error event on the page (not when the event was prevented with preventDefault(), or its handled is true because a view shows $error itself); hides on the next xo-done or after timeout seconds. Set detail.action ({ label, run }, for example Retry) and detail.timeout on the error and the toast shows that button, the same way an xo-notice does.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
timeout | number | 6 | Seconds before it hides (default 6, 0 = until the next success). |
Example
<xo-toast timeout="8"></xo-toast>
Guide Dialogs and modals
<xo-router>
A History-API router declared in markup: one <template xo-route> per route, optional <template xo-layout> shells. Links are intercepted, aria-current marks the active one. Guards and leave-vetoes are JavaScript (router.guard, router.beforeNavigate).
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
base | text | — | Path prefix the app lives under, for example /admin. |
base-links | flag | off | A root-relative link such as /users inside the router means /admin/users, so the same screens mount under any base. rel="external" leaves the app. |
Events
xo-navigateThe router moved to another location.xo-errorA load or an action failed.
Example
<xo-router base="/admin" base-links>
<template xo-layout="shell">
<nav><a href="/users">Users</a></nav>
<main xo-outlet></main>
</template>
<template xo-route="/users/:id" layout="shell" title="User">
<xo-view get="getPerson" p-id=":id"><h1>{name}</h1></xo-view>
</template>
<template xo-route="*">Not found</template>
</xo-router>
<xo-pager>
Previous and Next (and the "1–12 of 48" text) for any list in the enclosing <xo-view>: a tile grid, a thread list. The same pager <xo-table page-size> draws.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
page-size | number | — | Rows per page: sets limit; the buttons change offset. The response needs a total. |
page-sizes | list | — | Comma-separated rows-per-page choices. |
Example
<xo-view get="listPeople">
<ul><li xo-each="items">{name}</li></ul>
<xo-pager page-size="12" page-sizes="12,24,48"></xo-pager>
</xo-view>
Guide Recipes
<xo-tabs>
Accessible tabs from the sections that have xo-tab. Panels stay in the DOM, so form controls on other tabs still count; xo-reveal selects the tab of an invalid field.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
value | text | the first | Key of the selected tab (default: the first). |
label | text | — | Accessible name of the tab list. |
Events
xo-tab-changeAnother tab was selected.
Example
<xo-tabs label="Person">
<section xo-tab="General" id="general"><label>Name <input name="name"></label></section>
<section xo-tab="Topics" id="topics">…</section>
</xo-tabs>
Guide Tabs
<xo-combobox>
A form control to pick from a list you can filter by typing. <option> children are the choices; multiple shows chips; the value is a string, or an array with multiple.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
name | text | — | Form field path. |
multiple | flag | off | Pick several values (chips). |
creatable | flag | off | Allow a value that is not in the list. |
remote | flag | off | Do not filter locally: listen to xo-search and set el.options. |
options-op | operation | — | A GET operation whose list is the options (needs @xoong/data/view). With option-value and option-label (paths in each item, default id and name) and p-<name> parameters; a mutation of the list refreshes the options. |
option-value | path | id | With options-op: the path in each item that is the value (default id). |
option-label | path | name | With options-op: the path in each item that is the label (default name). |
placeholder | text | — | Hint text of the empty field. |
label | text | — | Accessible name when there is no <label>. |
required | flag | off | The field must be filled before the form can be sent. |
disabled | flag | off | Not available: not editable, not sent with the form. |
readonly | flag | off | Shown and sent, but not changeable. |
Events
xo-searchThe person typed in a combobox that does not filter by itself.
Example
<xo-combobox name="topics" label="Topics" multiple>
<option value="web">Web</option>
<option value="data">Data</option>
<option value="design">Design</option>
</xo-combobox>
Guide Combobox and tags
<xo-tags>
A free-text tag input: type and press Enter or a comma to add a chip; Backspace removes the last. Value: an array of strings.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
name | text | — | Form field path. |
label | text | — | Accessible name when there is no <label>. |
placeholder | text | — | Hint text of the empty field. |
required | text | — | The field must be filled before the form can be sent. |
disabled | text | — | Not available: not editable, not sent with the form. |
readonly | text | — | Shown and sent, but not changeable. |
Example
<xo-tags name="keywords" label="Keywords" placeholder="Add a keyword"></xo-tags>
Guide Combobox and tags
<xo-richtext>
A rich text editor form control: toolbar, headings, bold, italic, lists, quote, links. The value is sanitised HTML (p, h2, h3, strong, em, ul, ol, li, blockquote, br, a, img). xo-pick-media (cancelable) asks the app for an image.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
name | text | — | Form field path. |
tools | text | — | Space-separated tools: undo redo style bold italic underline ul ol quote link image (h2 as a toggle). |
toolbar | choice | — | full or compact forces a toolbar size; by default the editor's own width decides (full from 580px).One of full, compact. |
media | flag | off | Show the image tool; it fires xo-pick-media. |
placeholder | text | — | Hint text of the empty editor. |
label | text | — | Accessible name of the editor. |
maxlength | number | — | Maximum number of text characters. |
required | flag | off | The field must be filled before the form can be sent. |
disabled | flag | off | Not available: not editable, not sent with the form. |
readonly | flag | off | Shown and sent, but not changeable. |
Events
xo-pick-mediaThe image tool of the editor was used.xo-richtext-expandThe editor opened in its large dialog.xo-richtext-doneThe large editor dialog was closed.
Example
<xo-richtext name="bio" label="Bio" toolbar="compact" maxlength="2000" media></xo-richtext>
Guide Rich text
<xo-carousel>
A row of slides you move through: cards, a banner, a gallery. The children are the slides; they sit in a scroll container with scroll snap, so swiping is native. WAI-ARIA carousel pattern, slides out of view are inert.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
items | number | 1 | Slides visible at once (default 1; 1.15 lets the next peek). Also --xo-carousel-items in CSS. |
dots | flag | off | A button per position; a slide with data-thumb="url" shows that picture instead. |
counter | flag | off | Show "1 / 5". |
no-arrows | flag | off | Hide the previous and next buttons. |
loop | flag | off | Next at the end returns to the first slide. |
autoplay | number | — | Move on by itself every n milliseconds (pauses on hover and focus; off with reduced motion; adds a pause button). |
label | text | — | Accessible name of the carousel. |
Events
xo-carousel-changeThe carousel moved to another slide.
Example
<xo-carousel label="Services" items="3" dots counter loop>
<article>First</article>
<article>Second</article>
<article>Third</article>
<article>Fourth</article>
</xo-carousel>
Guide Carousel
<xo-switch>
One setting on or off (WAI-ARIA switch). A form control with a boolean value; the text inside is the visible label (label.xo-switch-row); change fires when the person toggles it.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
name | text | — | Form field path. |
checked | flag | off | Start on. |
label | text | — | Accessible name. |
size | choice | — | sm for the compact switch.One of sm. |
busy | flag | off | A save is running: aria-busy, clicks ignored. |
disabled | flag | off | Not available. |
readonly | flag | off | Shown but not changeable. |
required | flag | off | The field must be filled before the form can be sent. |
Example
<xo-switch name="notify" checked>Email me about new comments</xo-switch>
Guide More controls
<xo-date>
A date field with a calendar popover. The value is ISO YYYY-MM-DD (with range: { start, end }); the field shows the locale format and accepts typing.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
name | text | — | Form field path. |
label | text | — | Accessible name. |
min | text | — | Earliest date, YYYY-MM-DD. |
max | text | — | Latest date, YYYY-MM-DD. |
range | flag | off | Pick a start and an end (Clear and Apply in the calendar). |
week-start | number | the locale | First day of the week, 1 = Monday ... 7 = Sunday (default: the locale). |
placeholder | text | — | Hint text of the empty field. |
required | flag | off | The field must be filled before the form can be sent. |
disabled | flag | off | Not available: not editable, not sent with the form. |
readonly | flag | off | Shown and sent, but not changeable. |
Example
<xo-date name="publishedAt" label="Publish on" value="2026-11-25"></xo-date>
Guide More controls
<xo-time>
A time of day as two selects, hour and minute. The value is HH:mm or empty.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
name | text | — | Form field path. |
label | text | — | Accessible name of the group. |
step | number | 5 | Minutes between choices (default 5). |
min | text | — | Earliest time, HH:mm. |
max | text | — | Latest time, HH:mm. |
required | flag | off | The field must be filled before the form can be sent. |
disabled | flag | off | Not available: not editable, not sent with the form. |
readonly | flag | off | Shown and sent, but not changeable. |
Example
<xo-time name="at" label="Time" step="15" value="09:30"></xo-time>
Guide More controls
<xo-slider>
A number on a track (a native range input). input while dragging, change on release.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
name | text | — | Form field path. |
label | text | — | Accessible name. |
min | number | 0 | Lowest value (default 0). |
max | number | 100 | Highest value (default 100). |
step | number | 1 | Step (default 1). |
labels | list | — | Comma-separated names of the stops, read by screen readers (Low,Medium,High). |
required | flag | off | The field must be filled before the form can be sent. |
disabled | flag | off | Not available: not editable, not sent with the form. |
readonly | flag | off | Shown and sent, but not changeable. |
Example
<xo-slider name="quality" label="Image quality" min="0" max="100" step="5" value="80"></xo-slider>
Guide More controls
<xo-stepper>
A number with minus and plus buttons; arrows change by step, Page by ten steps, the value is clamped.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
name | text | — | Form field path. |
label | text | — | Accessible name. |
min | number | — | Lowest value. |
max | number | — | Highest value. |
step | number | 1 | Step (default 1). |
required | flag | off | The field must be filled before the form can be sent. |
disabled | flag | off | Not available: not editable, not sent with the form. |
readonly | flag | off | Shown and sent, but not changeable. |
Example
<xo-stepper name="perPage" label="Items per page" min="1" max="100" step="5" value="25"></xo-stepper>
Guide More controls
<xo-password>
A password field with a show/hide button.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
name | text | — | Form field path. |
label | text | — | Accessible name. |
autocomplete | text | — | new-password or current-password, for password managers. |
placeholder | text | — | Hint text of the empty field. |
required | flag | off | The field must be filled before the form can be sent. |
disabled | flag | off | Not available: not editable, not sent with the form. |
readonly | flag | off | Shown and sent, but not changeable. |
Example
<xo-password name="password" label="Password" autocomplete="new-password"></xo-password>
Guide More controls
<xo-copy>
A read-only value with a Copy button; fires copied.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
value | text | — | The text to show and copy. |
label | text | — | Accessible name of the field. |
required | flag | off | The field must be filled before the form can be sent. |
disabled | flag | off | Not available: not editable, not sent with the form. |
readonly | flag | off | Shown and sent, but not changeable. |
Events
copiedThe text was copied to the clipboard.copy-failedThe browser refused the clipboard.
Example
<xo-copy label="API key" value="xo_live_4f9a7f3a"></xo-copy>
Guide More controls
<xo-image>
An image that loads lazily, keeps its space (no layout jump), shows a placeholder while loading and a fallback on error. data-state: empty, loading, loaded, error.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
src | text | — | The image URL (in an xo-view: src="{cover.url}"). |
alt | text | — | Required: the text for the image, or alt="" when it is decorative. |
width | number | — | Intrinsic width, reserves the space with height. |
height | number | — | Intrinsic height. |
ratio | text | — | Aspect ratio to reserve instead of width/height: 16/9, 1.5. |
fit | text | cover | cover (default) or contain. |
position | text | — | object-position, e.g. top. |
srcset | text | — | Responsive sources, passed to the img. |
sizes | text | — | Display sizes for srcset. |
priority | flag | off | The image above the fold: loads at once with high priority (not lazy). |
loading | choice | lazy | lazy (default) or eager.One of lazy, eager. |
fallback | text | — | A src to use once when the image fails. |
Example
<xo-image src="img/landscape.svg" alt="A landscape" ratio="16/9" fallback="img/placeholder.svg"></xo-image>
Guide More controls
<xo-theme-toggle>
A button that switches between the light and the dark theme: sets data-theme on <html>, saves the choice, follows the system until chosen. Fires xo-theme-change.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
storage | text | xoong-theme | localStorage key (default xoong-theme). |
Events
xo-theme-changeThe theme was switched.
Example
<xo-theme-toggle></xo-theme-toggle>
Guide More controls
<xo-accordion>
A stack of native <details> as an accordion; single allows one open at a time; arrows move between summaries. Fires xo-accordion-change.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
single | flag | off | Only one section open at a time. |
Events
xo-accordion-changeA section of an accordion opened or closed.
Example
<xo-accordion single>
<details><summary>Does it need a build?</summary><div class="xo-accordion__body">No.</div></details>
<details><summary>Can I use my own styles?</summary><div class="xo-accordion__body">Yes.</div></details>
</xo-accordion>
Guide More controls
<xo-popover>
A small panel opened by a button: Escape or an outside click closes it and the focus returns to the trigger. xo-popover-open / xo-popover-close.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
anchor | selector | the element before it | Selector of the trigger (default: the element before it). |
label | text | — | Accessible name of the panel. |
open | flag | off | Open at the start. |
Events
xo-popover-openA popover opened.xo-popover-closeA popover closed.
Example
<div class="xo-notify">
<button aria-label="Notifications">Bell</button>
<xo-popover label="Notifications"><p>Nothing new.</p></xo-popover>
</div>
Guide More controls
<xo-consent>
A cookie consent bar that remembers the choice. The text inside is the message. Fires xo-consent with the choices.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
categories | list | — | Comma-separated categories: analytics,marketing. |
storage | text | xoong-consent | localStorage key (default xoong-consent). |
label | text | — | Accessible name of the bar. |
Events
xo-consentThe visitor made a choice about cookies.
Example
<xo-consent categories="analytics,marketing">
<b>We use two cookies.</b> One keeps you signed in, one measures anonymous visits.
</xo-consent>
<button xo-consent-open>Cookie settings</button>
Guide More controls
<xo-file>
A file field with a drop zone. The value is a File (an array with multiple). Refused files fire xo-file-reject.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
name | text | — | Form field path. |
label | text | — | Accessible name. |
accept | text | — | Allowed types: image/*, .pdf. |
max-size | text | — | Largest file: bytes or 2MB. |
multiple | flag | off | Several files. |
hint | text | — | Text under the prompt. |
required | flag | off | The field must be filled before the form can be sent. |
disabled | flag | off | Not available: not editable, not sent with the form. |
readonly | flag | off | Shown and sent, but not changeable. |
Events
xo-file-rejectA file was refused.
Example
<xo-file name="logo" label="Logo" accept="image/*" max-size="2MB" hint="PNG or SVG, up to 2 MB"></xo-file>
Guide More controls
<xo-command>
A command palette: Ctrl/Cmd+K, type to filter, arrows, Enter. The children (links, buttons with data-group) are the commands. Fires xo-command-run.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
shortcut | text | k | The key used with Ctrl/Cmd (default k). |
label | text | — | Accessible name of the dialog. |
Events
xo-command-runA command of the palette was run.
Example
<xo-command shortcut="k" label="Search commands">
<a href="/articles" data-group="Go to">Articles</a>
<button data-group="Actions" data-value="new-article">New article</button>
</xo-command>
Guide More controls
<xo-chart>
A bar or line chart drawn from data, with a table of the same numbers.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
type | choice | bar | bar (default) or line.One of bar, line. |
label | text | — | Accessible name of the chart. |
data | JSON | — | JSON: [{"label":"Mon","value":12}] or [12, 18]; or set el.rows. |
unit | text | — | Appended to values: k, %. |
heading | text | — | Label column of the table. |
value-heading | text | — | Value column of the table. |
Example
<xo-chart type="bar" label="Visits per day" unit="k" data='[{"label":"Mon","value":12},{"label":"Tue","value":18},{"label":"Wed","value":9}]'></xo-chart>
Guide More controls
<xo-range-slider>
A minimum and a maximum on one track: two native range inputs, thumbs cannot cross. The value is [min, max]; the attribute is value="20,70".
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
name | text | — | Form field path. |
label | text | — | Accessible name of the group. |
min | number | 0 | Lowest value (default 0). |
max | number | 100 | Highest value (default 100). |
step | number | 1 | Step (default 1). |
min-label | text | — | Accessible name of the minimum thumb. |
max-label | text | — | Accessible name of the maximum thumb. |
required | flag | off | The field must be filled before the form can be sent. |
disabled | flag | off | Not available: not editable, not sent with the form. |
readonly | flag | off | Shown and sent, but not changeable. |
Example
<xo-range-slider name="price" label="Price" min="0" max="100" value="20,70" min-label="Minimum price" max-label="Maximum price"></xo-range-slider>
Guide More controls
<xo-segmented>
A group of buttons where one is chosen. The children <button value> are the segments; aria-pressed on each; arrows move the focus.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
name | text | — | Form field path. |
label | text | — | Accessible name of the group. |
multiple | flag | off | Each segment toggles on its own; the value is an array. |
clearable | flag | off | A click on the chosen segment unchooses it. |
required | flag | off | The field must be filled before the form can be sent. |
disabled | flag | off | Not available: not editable, not sent with the form. |
readonly | flag | off | Shown and sent, but not changeable. |
Example
<xo-segmented name="period" label="Billing period" value="month">
<button value="month">Monthly</button>
<button value="year">Yearly</button>
</xo-segmented>
Guide More controls
<xo-split-button>
A main action with a menu of related actions. The first child is the main button; the others are the menu items.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
more | text | More options | Accessible name of the arrow button (default "More options"). |
Example
<xo-split-button more="More publish options">
<button class="primary">Publish</button>
<button>Schedule</button>
<button>Save as draft</button>
</xo-split-button>
Guide More controls
<xo-textarea>
A textarea with a character counter and a thin progress bar toward maxlength; over the limit it is invalid and the text is kept.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
name | text | — | Form field path. |
label | text | — | Accessible name. |
maxlength | number | — | Character limit. |
rows | number | 3 | Visible rows (default 3). |
placeholder | text | — | Hint text of the empty field. |
required | flag | off | The field must be filled before the form can be sent. |
disabled | flag | off | Not available: not editable, not sent with the form. |
readonly | flag | off | Shown and sent, but not changeable. |
Example
<xo-textarea name="excerpt" label="Excerpt" maxlength="160" rows="3"></xo-textarea>
Guide More controls
<xo-stat>
A KPI tile: label, value, a change against before and a sparkline from a series.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
label | text | — | What is measured. |
value | text | — | The number as text (formatted by you). |
delta | number | — | Change in percent; positive shows an up arrow, negative a down arrow. |
delta-label | text | — | Text after the change, e.g. "vs last week". |
series | list | — | Comma-separated numbers for the sparkline. |
trend | text | — | down-is-good when a fall is an improvement. |
Example
<xo-stat label="Visits" value="48,210" delta="12.4" delta-label="vs last week" series="20,22,18,26,24,32,29"></xo-stat>
Guide More controls
<xo-otp>
One-time code: one box per digit; the value is the digit string; fires complete when all are filled. Paste splits across the boxes.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
name | text | — | Form field path. |
label | text | — | Accessible name of the group. |
length | number | 6 | Number of digits (default 6). |
value | text | — | The start value. |
required | flag | off | Must be filled. |
disabled | flag | off | Not available. |
readonly | flag | off | Shown but not changeable. |
Events
completeEvery box of a one-time code is filled.
Example
<xo-otp name="code" length="6" label="Verification code"></xo-otp>
Guide More controls
<xo-avatar>
A round badge with initials from the name, or a picture; aria-label is the name and the status.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
name | text | — | Full name (initials and accessible name). |
src | text | — | Picture URL; the initials stay when it fails. |
size | choice | — | sm or lg.One of sm, lg. |
status | choice | — | ok, warn or muted.One of ok, warn, muted. |
Example
<xo-avatar name="Linh Phạm" status="ok"></xo-avatar>
Guide More controls
<xo-avatars>
A stack of avatars with a +N badge for the rest; a list.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
max | number | — | How many to show. |
label | text | — | Accessible name of the list. |
Example
<xo-avatars max="2" label="Reviewers">
<xo-avatar name="An Lê"></xo-avatar>
<xo-avatar name="Binh Vo"></xo-avatar>
<xo-avatar name="Chi Do"></xo-avatar>
</xo-avatars>
Guide More controls
<xo-progress>
A progress bar; no value means indeterminate.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
value | number | — | Current amount. |
max | number | 100 | Full amount (default 100). |
label | text | — | Accessible name. |
thin | flag | off | The thin variant. |
Example
<xo-progress value="62" label="Upload"></xo-progress>
Guide More controls
<xo-spinner>
A loading spinner, role=status; label="" makes it decorative.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
label | text | — | What is loading. |
size | choice | — | md or lg.One of md, lg. |
Example
<xo-spinner label="Loading" size="lg"></xo-spinner>
Guide More controls
<xo-steps>
Progress through a flow of steps: the children are the step titles; aria-current=step marks the current one; done steps show a check.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
current | number | — | Index of the current step (from 0). |
label | text | — | Accessible name of the list. |
Example
<xo-steps current="1" label="Checkout">
<span>Account</span><span>Plan</span><span>Payment</span>
</xo-steps>
Guide More controls
<xo-inline-edit>
Text you rename in place: select it or press the Rename button; Enter saves, Escape cancels. Fires save and cancel.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
name | text | — | Form field path. |
value | text | — | The text. |
label | text | — | Accessible name of the Rename button. |
disabled | flag | off | Not available. |
readonly | flag | off | Not editable. |
required | flag | off | The field must be filled before the form can be sent. |
Events
Example
<xo-inline-edit name="title" value="Greece Golden Visa" label="Title"></xo-inline-edit>
Guide More controls
<xo-drawer>
A side sheet on a native modal <dialog>: open it with xo-open="#id" like xo-dialog, close it with xo-close, Escape or (with dismiss) a click outside. The header and the footer (slot="footer") stay put, the body scrolls; full screen on a phone.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
heading | text | — | The title in the header (or give label). |
label | text | — | Accessible name when there is no heading. |
side | choice | end | end (default) or start: logical, follows the writing direction.One of end, start. |
size | choice | md | sm, md (default) or lg.One of sm, md, lg. |
dismiss | flag | off | Close it on a click outside. |
Example
<button xo-open="#filters">Filters</button>
<xo-drawer id="filters" heading="Filters" side="end" dismiss>
<p>Choose what to show.</p>
<button slot="footer" type="button" xo-close>Close</button>
</xo-drawer>
Guide More controls
<xo-breadcrumb>
The trail of pages that led here: <a> children or JSON items, the last is aria-current=page; the middle collapses into an "…" menu when max is set or the space is short.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
label | text | Breadcrumb | Accessible name of the navigation (default "Breadcrumb"). |
items | JSON | — | JSON: [{"label":"Home","href":"/"},{"label":"Now"}]; or write <a> children. |
max | number | — | Most items to show (the first and the last ones); the rest go into the "…" menu. |
Example
<xo-breadcrumb max="3" items='[{"label":"Home","href":"/"},{"label":"Docs","href":"/docs"},{"label":"Controls","href":"/docs/controls"},{"label":"Tree"}]'></xo-breadcrumb>
Guide More controls
<xo-alert>
A message that stays in the page: an icon by kind, an optional heading and action (slot="action"), a Close button with dismissible. role=alert for warning and danger, status otherwise. Fires xo-dismiss.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
kind | choice | info | info (default), success, warning or danger.One of info, success, warning, danger. |
heading | text | — | A bold first line. |
dismissible | flag | off | Show a Close button that hides the alert. |
live | choice | — | polite, assertive or off: overrides the role chosen by kind.One of polite, assertive, off. |
Events
xo-dismissThe Close button of an alert was pressed.
Example
<xo-alert kind="warning" heading="Your trial ends in 3 days" dismissible>Add a payment method to keep your pages.</xo-alert>
Guide More controls
<xo-skeleton>
A grey placeholder in the shape of what is loading: lines of text, a circle or a rectangle. Hidden from screen readers, with one visually hidden "Loading" status; the shimmer stops under reduced motion.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
lines | number | — | Number of text lines (the last is shorter). |
shape | choice | text | text (default), circle or rect.One of text, circle, rect. |
width | text | — | A CSS length; a bare number is pixels. |
height | text | — | A CSS length; a bare number is pixels. |
label | text | Loading | The status text (default "Loading"); label="" leaves it out. |
Example
<xo-skeleton lines="3"></xo-skeleton>
Guide More controls
<xo-tree>
A tree view (the WAI-ARIA tree pattern): nested <ul> or JSON data; arrows, Home, End, * and type-ahead; selectable or multiple (checkboxes, a parent is true, false or mixed). Fires xo-tree-select, xo-tree-toggle and, for lazy items, xo-tree-load.
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
data | JSON | — | JSON: [{"id":"a","label":"A","children":[...]}]; or write nested <ul><li>. |
label | text | — | Accessible name of the tree. |
selectable | flag | off | One item can be chosen (aria-selected). |
multiple | flag | off | Every item has a checkbox; a parent follows its children. |
expanded | list | — | Ids of the items open at the start, separated by commas. |
selected | list | — | Ids chosen at the start, separated by commas. |
Events
xo-tree-selectThe choice in a tree changed.xo-tree-toggleA tree item opened or closed.xo-tree-loadA lazy tree item was opened for the first time.
Example
<xo-tree label="Files" selectable>
<ul><li>src<ul><li>index.js</li><li>util.js</li></ul></li><li>README.md</li></ul>
</xo-tree>
Guide More controls
<xo-color>
A color field: a swatch button opens a panel with the native color picker, a validated hex field and preset swatches. The value is #rrggbb (#rrggbbaa with alpha).
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
name | text | — | Form field path. |
label | text | — | Accessible name. |
alpha | flag | off | Add an opacity slider; the value then has two more digits. |
swatches | list | — | Preset colors: #0b7285,#c92a2a. |
required | flag | off | The field must be filled before the form can be sent. |
disabled | flag | off | Not available: not editable, not sent with the form. |
readonly | flag | off | Shown and sent, but not changeable. |
Example
<xo-color name="brand" label="Brand color" value="#0b7285" swatches="#0b7285,#c92a2a,#2b8a3e"></xo-color>
Guide More controls
<xo-rating>
A star rating: one native radio per star (two with half), so arrows, Tab and screen readers work; the pointer previews; the value is a number (0 is nothing).
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
name | text | — | Form field path. |
label | text | Rating | Accessible name of the group (default "Rating"). |
max | number | 5 | Number of stars (default 5). |
half | flag | off | Allow half stars. |
clearable | flag | off | A click on the chosen star, or Delete, sets it back to nothing. |
required | flag | off | The field must be filled before the form can be sent. |
disabled | flag | off | Not available: not editable, not sent with the form. |
readonly | flag | off | Shown and sent, but not changeable. |
Example
<xo-rating name="score" label="Your rating" value="4" half clearable></xo-rating>
Guide More controls
Directives
The xo-* attributes you put on your own markup: data, actions, form rows, routing, dialogs. 35 entries.
xo-eachxo-ifxo-paramxo-refreshxo-clickxo-submitxo-bodyxo-keepxo-optimisticxo-selectxo-selectedxo-withxo-confirmxo-noticexo-thenxo-confirm-titlexo-confirm-okxo-confirm-cancelxo-confirm-kindxo-addxo-removexo-movexo-dragxo-guardxo-routexo-layoutxo-outletxo-currentxo-openxo-closexo-tabxo-menu-triggerxo-tooltipxo-consent-openxo-t
xo-each
Repeat this element for each item of the list at the given path (keyed by entity id).
- Applies to
- Any element inside
<xo-view>. On a<template>it repeats the template's children. - Value
- The path of a list:
items,tags,blocks.
- Inside it
{path}reads the current item,$indexis its position from 0 and$nfrom 1. - Rows are keyed by entity id, so a changed list updates the rows that changed.
Example
<xo-view get="listPeople">
<ul><li xo-each="items">{$n}. {name}</li></ul>
</xo-view>
Guide Data and tables
xo-if
Render this element only when the path is truthy. Prefix with ! to negate. An empty array is false.
- Applies to
- Any element inside
<xo-view>. - Value
- A path (
$loading,items),!pathto negate, or a comparison with a text:type=image,status!=draft.
Example
<xo-view get="listPeople">
<p xo-if="$loading">Loading…</p>
<p xo-if="!items">Nobody here yet.</p>
</xo-view>
Guide Loading, empty and error
xo-param
On an <input> or <select>: set the view parameter of that name to its value. On other elements, on click, set a view parameter and refetch: name=value, name+=n, name-=n, or name~=value (toggle value / -value, for sorting).
- Applies to
- An
<input>or<select>(its value), or any element that is clicked (name=value), inside<xo-view>. - Value
- On a field: the parameter name. On click:
name=value,name+=n,name-=n(never below 0) orname~=value(togglevalueand-value, for sorting).
- A button whose
xo-parammatches the current value getsaria-current.
Example
<xo-view get="listPeople" url-params="name,role">
<input type="search" xo-param="name" aria-label="Search by name">
<button xo-param="role=admin">Admins</button>
<button xo-param="role=editor">Editors</button>
<ul><li xo-each="items">{name}</li></ul>
</xo-view>
Guide Data and tables
xo-refresh
On a button inside an <xo-view>: on click, fetch the view's data again (a "Try again" button after an error, or a refresh button).
- Applies to
- A button inside
<xo-view>. - Value
- None.
Example
<xo-view get="listPeople">
<p xo-if="$error">Could not load. <button xo-refresh>Try again</button></p>
</xo-view>
Guide Loading, empty and error
xo-click
Call an operation when clicked: "METHOD /path/{param}" or an operationId. Params come from the current item, then the view's p-* params.
- Applies to
- A
<button>or link inside<xo-view>. Another element works, but it is not reachable by keyboard. - Value
- An operationId (
deletePerson) orMETHOD /path/{param}.{param}comes from the current item, then the view'sp-*parameters.
- An operation that reads (GET) belongs on
<xo-view get>, not onxo-click. - After success the queries that overlap the path are refetched.
Example
<xo-view get="listPeople">
<ul>
<li xo-each="items">{name}
<button xo-click="deletePerson" xo-confirm="Delete {name}?" xo-notice="Deleted">Delete</button>
</li>
</ul>
</xo-view>
Guide Data and tables
xo-submit
On a <form>: send the form fields as the request body of the operation. The form is reset on success unless xo-keep is present. Field errors from the server appear beside the inputs.
- Applies to
- A
<form>inside<xo-view>. - Value
- An operationId or
METHOD /path/{param}.
- The field names are paths (
name,title.en,items[0].x). The form is reset after success unlessxo-keepis present.
Example
<xo-view>
<form xo-submit="createPerson" xo-notice="Person added">
<label>Name <input name="name" required></label>
<label>E-mail <input name="email" type="email"></label>
<button>Add</button>
</form>
</xo-view>
Guide Forms
xo-body
With xo-click: take the request body from the item at this path.
- Applies to
- An element with
xo-click. - Value
- The path of the object to send:
.(the current item),draft,$selected.
Example
<xo-view get="listPeople">
<ul><li xo-each="items">{name}
<button xo-click="updatePerson" xo-body=".">Save</button>
</li></ul>
</xo-view>
xo-keep
With xo-submit: keep the field values after a successful send.
- Applies to
- A
<form>that hasxo-submit. - Value
- None.
Example
<xo-view>
<form xo-submit="createPerson" xo-keep>
<input name="name" aria-label="Name">
<button>Add</button>
</form>
</xo-view>
xo-optimistic
On xo-click: set these fields (field=value,field2={path}) on the current item immediately, and undo them if the call fails.
Example
<xo-view get="listPeople">
<ul><li xo-each="items">{name} ({role})
<button xo-click="updatePerson" xo-with="role=viewer" xo-optimistic="role=viewer">Make viewer</button>
</li></ul>
</xo-view>
Guide Data and tables
xo-select
On a checkbox in a row (inside xo-each): tick that row. all:items in the header ticks every row of the list. <xo-table selectable> does this for you.
- Applies to
- A checkbox in a row (inside
xo-each), or in the header. - Value
- None on a row;
all:items(all:and the list path) on the header checkbox ticks every row.
<xo-table selectable>adds these checkboxes for you.
Example
<xo-view get="listPeople">
<label><input type="checkbox" xo-select="all:items"> All</label>
<ul><li xo-each="items"><label><input type="checkbox" xo-select> {name}</label></li></ul>
<button xo-click="deletePerson" xo-selected xo-if="$selectedCount">Delete {$selectedCount}</button>
</xo-view>
Guide Data and tables
xo-selected
On an xo-click button: run the operation once for every ticked row, one after another; the first failure stops the rest. With a value (xo-selected="ids"): ONE request whose body has the ticked ids in that field.
- Applies to
- An element with
xo-click. - Value
- None: run the operation once per ticked row. With a field name (
ids): one request whose body has the ticked ids in that field.
Example
<xo-view get="listPeople">
<ul><li xo-each="items"><label><input type="checkbox" xo-select> {name}</label></li></ul>
<button xo-click="deletePerson" xo-selected>Delete ticked</button>
</xo-view>
Guide Data and tables
xo-with
On xo-click/xo-submit: fixed fields added to the request body, field=value,other={path} (typed from the request schema).
- Applies to
- An element with
xo-clickorxo-submit, orxo-addin a form. - Value
- A list
field=value,other={path}. Values are typed from the request schema.
Example
<xo-view get="listPeople">
<ul><li xo-each="items">{name}
<button xo-click="updatePerson" xo-with="role=admin">Make admin</button>
</li></ul>
</xo-view>
Guide Data and tables
xo-confirm
On xo-click/xo-submit: ask before running, with a native dialog. {path} names the item: xo-confirm="Delete {title}?".
- Applies to
- An element with
xo-clickorxo-submit. - Value
- The question;
{path}names the item:Delete {name}?.
- The dialog is native. With @xoong/ui imported it is the built-in confirm dialog;
xo-confirm-title,xo-confirm-ok,xo-confirm-cancelandxo-confirm-kindtune it. - In JavaScript the same question is
confirm().
Example
<xo-view get="listPeople">
<ul><li xo-each="items">{name}
<button xo-click="deletePerson" xo-confirm="Delete {name}?" xo-confirm-title="Delete person" xo-confirm-ok="Delete" xo-confirm-kind="danger">Delete</button>
</li></ul>
</xo-view>
Guide Dialogs and modals
xo-notice
On xo-click/xo-submit: after success, show this message in <xo-toast> ({path} and {$selectedCount} work).
- The message is shown by
<xo-toast>. It travels as thexo-noticeevent.
Example
<button xo-click="deletePerson" xo-notice="Deleted {name}">Delete</button>
Guide Dialogs and modals
xo-then
On xo-click/xo-submit: after success, go to this path; {id} comes from the result (a created record), then from the item acted on: xo-then="/articles/{id}", or xo-then="/articles" after a delete. <xo-router> navigates.
- Applies to
- An element with
xo-clickorxo-submit. - Value
- A path of this site.
{id}comes from the result (a created record), then from the item acted on.
<xo-router>navigates. Without a router the page is loaded withlocation.assign. Only a relative path or a same-origin URL is followed.
Example
<xo-view>
<form xo-submit="createPerson" xo-then="/people/{id}">
<input name="name" aria-label="Name">
<button>Create</button>
</form>
</xo-view>
xo-confirm-title
With xo-confirm: a heading for the question (xo-confirm-title="Delete person").
- Applies to
- An element that has
xo-confirm. - Value
- The heading text.
Example
<button xo-click="deletePerson" xo-confirm="This cannot be undone." xo-confirm-title="Delete person">Delete</button>
Guide Dialogs and modals
xo-confirm-ok
With xo-confirm: the text of the confirming button (xo-confirm-ok="Delete") instead of "Confirm".
- Applies to
- An element that has
xo-confirm. - Value
- The text of the confirming button.
Example
<button xo-click="deletePerson" xo-confirm="Delete {name}?" xo-confirm-ok="Delete">Delete</button>
Guide Dialogs and modals
xo-confirm-cancel
With xo-confirm: the text of the cancelling button instead of "Cancel".
- Applies to
- An element that has
xo-confirm. - Value
- The text of the cancelling button.
Example
<button xo-click="deletePerson" xo-confirm="Delete {name}?" xo-confirm-cancel="Keep it">Delete</button>
xo-confirm-kind
With xo-confirm: danger, warning, info or success: an icon and the matching colour; the confirming button is red unless a kind says otherwise.
- Applies to
- An element that has
xo-confirm. - Value
- One of
danger,warning,info,success. - Values
danger,warning,info,success
Example
<button xo-click="updatePerson" xo-confirm="Publish now?" xo-confirm-kind="info">Publish</button>
Guide Dialogs and modals
xo-add
In a form: append an item to the array at this path (blocks); xo-with="type=image" gives a union item its tag.
- Applies to
- A button inside a
<form xo-submit>. - Value
- The path of the array:
keywords,blocks. Addxo-with="type=image"to give a union item its tag.
Example
<xo-view get="getPerson" p-id="1">
<form xo-submit="updatePerson">
<div xo-each="keywords"><input name="keywords[{$index}]" aria-label="Keyword"></div>
<button type="button" xo-add="keywords">Add keyword</button>
</form>
</xo-view>
Guide Forms
xo-remove
In a form row: remove the array item at this path (blocks[{$index}]).
- Applies to
- A button inside a row of an array in a form.
- Value
- The path of the item:
keywords[{$index}].
Example
<xo-view get="getPerson" p-id="1">
<form xo-submit="updatePerson">
<div xo-each="keywords"><input name="keywords[{$index}]" aria-label="Keyword">
<button type="button" xo-remove="keywords[{$index}]" aria-label="Remove">✕</button></div>
</form>
</xo-view>
Guide Forms
xo-move
In a form row: move the array item at this path, blocks[{$index}]:up or :down.
- Applies to
- A button inside a row of an array in a form.
- Value
- The path of the item and a direction:
keywords[{$index}]:upor:down.
Example
<xo-view get="getPerson" p-id="1">
<form xo-submit="updatePerson">
<div xo-each="keywords"><input name="keywords[{$index}]" aria-label="Keyword">
<button type="button" xo-move="keywords[{$index}]:up" aria-label="Move up">↑</button>
<button type="button" xo-move="keywords[{$index}]:down" aria-label="Move down">↓</button></div>
</form>
</xo-view>
Guide Forms
xo-drag
On a handle inside an array row of a form: drag the row onto another row of the same list to move it (blocks[{$index}]). The xo-move buttons stay for keyboards.
- Applies to
- A handle inside a row of an array in a form.
- Value
- The path of the item:
keywords[{$index}].
Example
<xo-view get="getPerson" p-id="1">
<form xo-submit="updatePerson">
<div xo-each="keywords"><span class="handle" xo-drag="keywords[{$index}]" title="Drag to reorder">⠿</span>
<input name="keywords[{$index}]" aria-label="Keyword"></div>
</form>
</xo-view>
Guide Forms
xo-guard
On a <form xo-submit>: with unsaved changes the router asks before the page is left. xo-guard="off" disables it; any other text is the question to ask.
- Applies to
- A
<form xo-submit>. - Value
offdisables the question; any other text is the question to ask.
Example
<xo-view get="getPerson" p-id="1">
<form xo-submit="updatePerson" xo-guard="Leave without saving?">
<input name="name" aria-label="Name"> <button>Save</button>
</form>
</xo-view>
xo-route
On a <template> inside <xo-router>: the path this route answers, for example /users/:id; * is the not-found route. The content is inert until the route matches. An empty template with src fetches that URL the first time the route (or a layout) is shown.
- Applies to
- A
<template>inside<xo-router>. - Value
- The path the route answers:
/users/:id;*is the not-found route.
Example
<xo-router>
<template xo-route="/users/:id" title="User"><xo-view get="getPerson" p-id=":id"><h1>{name}</h1></xo-view></template>
<template xo-route="*">Not found</template>
</xo-router>
xo-layout
On a <template> inside <xo-router>: a layout (shell) that routes use with layout="name". It must contain one element with xo-outlet.
- Applies to
- A
<template>inside<xo-router>. - Value
- The name of the layout. Routes use it with
layout="name".
Example
<xo-router>
<template xo-layout="shell"><nav><a href="/users">Users</a></nav><main xo-outlet></main></template>
<template xo-route="/users" layout="shell">Users</template>
</xo-router>
xo-outlet
In a layout: the element where the matched route content is shown. The layout stays while only this content changes.
- Applies to
- One element in a layout.
- Value
- None.
Example
<xo-router>
<template xo-layout="shell"><header>My app</header><main xo-outlet></main></template>
<template xo-route="/" layout="shell">Home</template>
</xo-router>
xo-current
On a link, a nav, or the router: leave aria-current as the author set it. The router then does not mark that link current.
- Applies to
- A link, a nav, or
<xo-router>. - Value
- Present. No value.
Example
<nav xo-current>
<a href="/studio/pages" aria-current="page">Pages</a>
</nav>
xo-open
Open the <xo-dialog> matched by this selector, for example #add.
- Applies to
- A button anywhere in the page.
- Value
- A selector for an
<xo-dialog>:#add.
Example
<button xo-open="#add">Add person</button>
<xo-dialog id="add" heading="Add person"><xo-form op="createPerson"></xo-form></xo-dialog>
Guide Dialogs and modals
xo-close
Close the <xo-dialog> this element is in.
- Applies to
- A button inside an
<xo-dialog>. - Value
- None.
Example
<xo-dialog id="info" heading="Info">
<p>Saved.</p>
<button slot="footer" xo-close>Close</button>
</xo-dialog>
Guide Dialogs and modals
xo-tab
On a section inside <xo-tabs>: the tab label. The section id is the tab key (its position when it has none).
- Applies to
- A
<section>inside<xo-tabs>. - Value
- The tab label. The section's
idis the key of the tab (its position without one).
Example
<xo-tabs label="Page"><section xo-tab="General">…</section><section xo-tab="SEO" id="seo">…</section></xo-tabs>
Guide Tabs
xo-tooltip
On any element: a short description shown on hover and focus (and hidden on Escape). It is a description, not the accessible name.
- Applies to
- Any element.
- Value
- The text of the tooltip.
- It describes the element; it is not its accessible name, so a button without text still needs
aria-label.
Example
<button xo-tooltip="Copy the link" aria-label="Copy link">Copy</button>
Guide Menu and tooltip
xo-consent-open
On any element: a click shows the <xo-consent> bar again (cookie settings).
- Applies to
- Any element (a button).
- Value
- None.
Example
<button xo-consent-open>Cookie settings</button>
Guide More controls
xo-t
Translate this element with @xoong/i18n: xo-t="key" for its text, xo-t="attr:key; attr2:key2" for attributes. The text written in the HTML is the default. {placeholders} come from the enclosing xo-view.
- Applies to
- Any element.
- Value
- A message key for the text (
home.title), orattr:key; attr2:key2for attributes.
- The text written in the HTML is the default. Inside an
<xo-view>,{placeholders}come from the view.
Example
<h1 xo-t="home.title">Welcome</h1>
<input xo-t="placeholder:search.placeholder" placeholder="Search" aria-label="Search">
Guide Recipes
Events
The events xoong elements fire, what is in detail, and who listens. 36 entries.
xo-donexo-errorxo-invalidxo-noticexo-goxo-navigatexo-columns-changexo-dirtyxo-revealxo-array-changexo-form-readyxo-pick-mediaxo-richtext-expandxo-richtext-donexo-searchxo-tab-changexo-menu-selectxo-menu-openxo-menu-closexo-carousel-changexo-file-rejectxo-command-runxo-consentxo-theme-changexo-popover-openxo-popover-closexo-accordion-changecopiedcopy-failedcompletesavexo-dismissxo-tree-selectxo-tree-togglexo-tree-loadcancel
xo-done
An action succeeded. Fires on the element that ran the action, after the server answered, and bubbles. <xo-dialog> closes when a form inside it fires it; <xo-toast> hides its error.
Example
document.addEventListener('xo-done', (event) => console.log(event.detail));
xo-error
A load or an action failed. Fires on the element that ran the action (or the view that could not load) and bubbles. Cancelable: call preventDefault() when your own code handles the failure, and <xo-toast> stays quiet. event.handled is true when the view shows $error itself, or the error is a 401 that onUnauthorized deals with: the toast skips those too. detail.action ({ label, run }, for example Retry) and detail.timeout are drawn when the toast is not skipped.
- Fired by
<xo-view>,<xo-router>detail- The
XoongError(status, code, detail, fields). Setaction({ label, run }) andtimeouton it and<xo-toast>shows that button (it reads them one microtask later).
Example
document.addEventListener('xo-error', (event) => console.log(event.detail));
xo-invalid
A form was not sent because it is invalid. Nothing was sent; the form already shows why beside each field.
- Fired by
<form xo-submit>detail{ errors }: the messages by field path.
Example
document.addEventListener('xo-invalid', (event) => console.log(event.detail));
xo-notice
A message for the toast. Fires after an action with xo-notice succeeded, and when you call toast.show(). <xo-toast> listens on the document.
- Fired by
xo-notice,toast.show()detail{ message, kind };toast.show()addsactionandtimeout.
Example
document.addEventListener('xo-notice', (event) => console.log(event.detail));
xo-go
An action asked to go to another page. Cancelable. <xo-router> handles it and cancels the default; without a router the page is loaded with location.assign.
- Fired by
xo-thendetail{ path }: where to go.
Example
document.addEventListener('xo-go', (event) => console.log(event.detail));
xo-columns-change
The person showed or hid a column of the table. Fires from the Columns picker. With columns-key the choice is also remembered in this browser.
- Fired by
<xo-table column-picker>detail{ visible, hidden }: the field names that are shown and those that are hidden.
Example
document.addEventListener('xo-columns-change', (event) => console.log(event.detail));
xo-dirty
A form gained or lost unsaved changes. The router listens to it to ask before the page is left.
- Fired by
<form xo-submit>detail{ dirty }: true when there are unsaved changes.
Example
document.addEventListener('xo-dirty', (event) => console.log(event.detail));
xo-reveal
The form is about to focus an invalid field. Cancelable. <xo-tabs> listens and selects the tab of the field first, so the field is visible when it gets the focus.
- Fired by
<form xo-submit>detail{ path }: the invalid field.
Example
document.addEventListener('xo-reveal', (event) => console.log(event.detail));
xo-array-change
A row of an array in a form was added, removed or moved. Fires for the add, remove and move buttons, a drag, and code that changes the array.
- Fired by
<form xo-submit>detail{ path, op, from, to }:opisadd,removeormove.
Example
document.addEventListener('xo-array-change', (event) => console.log(event.detail));
xo-form-ready
The form model exists. From here form.model is available. A child control that needs the model listens for this event.
- Fired by
<form xo-submit>detail{ model }: the form model.
Example
document.addEventListener('xo-form-ready', (event) => console.log(event.detail));
xo-pick-media
The image tool of the editor was used. Cancelable. Open your media library in a listener, call preventDefault(), and call detail.insert now or later. Without preventDefault() nothing is inserted.
- Fired by
<xo-richtext>detail{ insert }: callinsert({ src, alt })when the person has chosen.
Example
document.addEventListener('xo-pick-media', (event) => console.log(event.detail));
xo-richtext-expand
The editor opened in its large dialog.
- Fired by
<xo-richtext>detail- None.
Example
document.addEventListener('xo-richtext-expand', (event) => console.log(event.detail));
xo-richtext-done
The large editor dialog was closed. Fires for Done and for Escape.
- Fired by
<xo-richtext>detail- None.
Example
document.addEventListener('xo-richtext-done', (event) => console.log(event.detail));
xo-search
The person typed in a combobox that does not filter by itself. Set el.options in the listener.
- Fired by
<xo-combobox remote>detail{ query }: what the person typed.
Example
document.addEventListener('xo-search', (event) => console.log(event.detail));
xo-tab-change
Another tab was selected.
- Fired by
<xo-tabs>detail{ value }: the key of the selected tab.
Example
document.addEventListener('xo-tab-change', (event) => console.log(event.detail));
xo-carousel-change
The carousel moved to another slide.
- Fired by
<xo-carousel>detail{ index, count }: the first visible slide and the number of slides.
Example
document.addEventListener('xo-carousel-change', (event) => console.log(event.detail));
xo-file-reject
A file was refused.
- Fired by
<xo-file>detail{ file, reason }:reasonistypeorsize.
Example
document.addEventListener('xo-file-reject', (event) => console.log(event.detail));
xo-command-run
A command of the palette was run.
- Fired by
<xo-command>detail{ value, label }:data-value(or the href), and the text of the command.
Example
document.addEventListener('xo-command-run', (event) => console.log(event.detail));
xo-consent
The visitor made a choice about cookies.
- Fired by
<xo-consent>detail- The choices: one boolean per category (
{ analytics: true, marketing: false }).
Example
document.addEventListener('xo-consent', (event) => console.log(event.detail));
xo-theme-change
The theme was switched.
- Fired by
<xo-theme-toggle>detail{ theme }:lightordark.
Example
document.addEventListener('xo-theme-change', (event) => console.log(event.detail));
xo-popover-open
A popover opened.
- Fired by
<xo-popover>detail- None.
Example
document.addEventListener('xo-popover-open', (event) => console.log(event.detail));
xo-popover-close
A popover closed.
- Fired by
<xo-popover>detail- None.
Example
document.addEventListener('xo-popover-close', (event) => console.log(event.detail));
xo-accordion-change
A section of an accordion opened or closed.
- Fired by
<xo-accordion>detail{ index, open }: which section and whether it is open now.
Example
document.addEventListener('xo-accordion-change', (event) => console.log(event.detail));
copied
The text was copied to the clipboard.
- Fired by
<xo-copy>detail{ text }: what was copied.
Example
document.addEventListener('copied', (event) => console.log(event.detail));
copy-failed
The browser refused the clipboard.
- Fired by
<xo-copy>detail{ text }: what could not be copied.
Example
document.addEventListener('copy-failed', (event) => console.log(event.detail));
complete
Every box of a one-time code is filled.
- Fired by
<xo-otp>detail{ value }: the digits.
Example
document.addEventListener('complete', (event) => console.log(event.detail));
save
The new text of an inline edit was saved. Enter saves.
- Fired by
<xo-inline-edit>detail{ value }: the new text.
Example
document.addEventListener('save', (event) => console.log(event.detail));
xo-dismiss
The Close button of an alert was pressed.
- Fired by
<xo-alert>detail- None.
preventDefault()keeps the alert.
Example
document.addEventListener('xo-dismiss', (event) => console.log(event.detail));
xo-tree-select
The choice in a tree changed.
- Fired by
<xo-tree>detail{ id, selected }: the item acted on and the chosen ids.
Example
document.addEventListener('xo-tree-select', (event) => console.log(event.detail));
xo-tree-toggle
A tree item opened or closed.
- Fired by
<xo-tree>detail{ id, expanded }: the item and whether it is open now.
Example
document.addEventListener('xo-tree-toggle', (event) => console.log(event.detail));
xo-tree-load
A lazy tree item was opened for the first time.
- Fired by
<xo-tree>detail{ id }: the lazy item; answer withtree.setChildren(id, children).
Example
document.addEventListener('xo-tree-load', (event) => console.log(event.detail));
cancel
An inline edit was cancelled. Escape cancels.
- Fired by
<xo-inline-edit>detail- None.
Example
document.addEventListener('cancel', (event) => console.log(event.detail));
JavaScript API
The functions and objects you import when HTML is not enough. 46 entries.
configure()getClient()createClient()client.load()client.query()client.mutate()client.seed()client.stream()client.invalidateAll()client.reset()derived()connectStream()XoongErrorsetFormat()confirm()alert()prompt()ask()confirmLabelssetConfirm()toast.show()router.navigate()router.guard()router.beforeNavigate()router.currentconfigureI18n()setLocale()t()localeaddMessages()onLocaleChange()detectLocale()signal()computed()effect()batch()untracked()html()render()XoElementdefine()XoControlcreateForm()attach()setMessages()sanitizeHtml()
configure(options)
Every <xo-view>, xo-click and xo-submit uses this client. Call it before the views start; views that started earlier wait for it.
- Returns
Client
Parameters
| Name | Type | What it is |
|---|---|---|
api | object | Required. The contents of .xoong/operations.json (written by xoong sync). |
baseUrl | string | Prefix of every request URL, for example /api. |
headers | function | Returns the headers of a request, or a promise of them: authentication. |
credentials | string | The fetch credentials mode (same-origin, include, omit). |
staleTime | number | Milliseconds a GET result stays fresh. Default 30000. |
gcTime | number | Milliseconds an unobserved query is kept. Default 300000. |
evictEntities | boolean | Drop the entities that no cached query refers to when a query is collected. Default true. |
retry | number | Retries of a GET after a network error or a 502, 503 or 504. Default 2. |
retryDelay | number | Base delay in milliseconds, doubled per retry. Default 300. |
invalidate | object | Which queries a mutation refreshes, by operation: { shipOrder: ['getDashboard'] }. |
onError | function | Called with every XoongError. |
onUnauthorized | function | Called with a 401 error (onError is called too). |
streamUrl | string | The server-sent events endpoint that <xo-view live> listens to. |
fetch | function | Your own fetch, for tests or a custom transport. |
Example
import { configure } from '@xoong/data';
import '@xoong/data/view';
import operations from '/.xoong/operations.json' with { type: 'json' };
configure({ api: operations, baseUrl: '/api', streamUrl: '/api/events' });
TypeScript declaration
function configure<Ops extends Record<keyof Ops, OperationType> = any>(options: ClientOptions): Client<Ops>;Guide Install
getClient()
Use it when HTML is not enough: getClient().load(...), .mutate(...), .reset().
- Returns
Client
Example
import { getClient } from '@xoong/data';
const people = await getClient().load('listPeople', { limit: 5 });
TypeScript declaration
function getClient<Ops extends Record<keyof Ops, OperationType> = any>(): Client<Ops>;Guide Install
createClient(options)
Takes the same options as configure(). Useful in tests and for a second API.
- Returns
Client
Parameters
| Name | Type | What it is |
|---|---|---|
api | object | Required. The contents of .xoong/operations.json (written by xoong sync). |
baseUrl | string | Prefix of every request URL, for example /api. |
headers | function | Returns the headers of a request, or a promise of them: authentication. |
credentials | string | The fetch credentials mode (same-origin, include, omit). |
staleTime | number | Milliseconds a GET result stays fresh. Default 30000. |
gcTime | number | Milliseconds an unobserved query is kept. Default 300000. |
evictEntities | boolean | Drop the entities that no cached query refers to when a query is collected. Default true. |
retry | number | Retries of a GET after a network error or a 502, 503 or 504. Default 2. |
retryDelay | number | Base delay in milliseconds, doubled per retry. Default 300. |
invalidate | object | Which queries a mutation refreshes, by operation: { shipOrder: ['getDashboard'] }. |
onError | function | Called with every XoongError. |
onUnauthorized | function | Called with a 401 error (onError is called too). |
streamUrl | string | The server-sent events endpoint that <xo-view live> listens to. |
fetch | function | Your own fetch, for tests or a custom transport. |
Example
import { createClient } from '@xoong/data';
const client = createClient({ api: operations, baseUrl: '/api' });
TypeScript declaration
function createClient<Ops extends Record<keyof Ops, OperationType> = any>(options: ClientOptions): Client<Ops>;client.load(operation, params?)
Uses the cache while the result is fresh (staleTime). Throws a XoongError when the request fails.
- Returns
Promise<response>
Parameters
| Name | Type | What it is |
|---|---|---|
operation | string | An operationId or GET /path. |
params | object | Path and query parameters. |
Example
const page = await getClient().load('listPeople', { limit: 10, sort: 'name' });
TypeScript declaration
load<K extends Id<Ops>>(op: K, ...params: ParamsArg<Ops[K]>): Promise<Ops[K]['response']>;Guide Data and tables
client.query(operation, params?)
data, status (idle, loading, success, error) and error are signals: read them inside an effect or a template. retain() and release() keep the query alive while you observe it.
- Returns
{ data, status, error, retain(), release(), invalidate(), fetch() }
Parameters
| Name | Type | What it is |
|---|---|---|
operation | string | An operationId or GET /path. |
params | object | Path and query parameters. |
Example
const query = getClient().query('listPeople', { limit: 5 });
query.retain();
effect(() => console.log(query.status.value, query.data.value));
TypeScript declaration
query<K extends Id<Ops>>(op: K, ...params: ParamsArg<Ops[K]>): TypedQuery<Ops[K]>;client.mutate(operation, params, body?)
Resolves with the response. The queries whose path overlaps the mutation are refetched afterwards (see invalidate in configure).
- Returns
Promise<response>
Parameters
| Name | Type | What it is |
|---|---|---|
operation | string | An operationId or METHOD /path. |
params | object | Path parameters. |
body | object | The request body. |
Example
await getClient().mutate('createPerson', {}, { name: 'Ada Lovelace', email: 'ada@example.org' });
TypeScript declaration
mutate<K extends Id<Ops>>(op: K, params: Ops[K]['params'], ...body: [Ops[K]['body']] extends [never] ? [] : [body: Ops[K]['body']]): Promise<Ops[K]['response']>;Guide Data and tables
client.seed(operation, params, data)
As if the GET had run: no request is made until the result is stale. For data embedded in the page.
- Returns
the data
Parameters
| Name | Type | What it is |
|---|---|---|
operation | string | An operationId. |
params | object | The parameters the GET would have had. |
data | object | The response. |
Example
getClient().seed('listPeople', { limit: 5 }, { items: [], total: 0 });
TypeScript declaration
seed<K extends Id<Ops>>(op: K, params: Ops[K] extends { params: infer P } ? P : never, data: Ops[K]['response']): Ops[K]['response'];client.stream(operation, params, body?, { signal }?)
Each event is { event, data, json, id }; json is data parsed when it is JSON. Pass an AbortSignal to stop.
- Returns
AsyncGenerator<SseEvent>
Parameters
| Name | Type | What it is |
|---|---|---|
operation | string | An operationId. |
params | object | Path parameters. |
body | object | The request body. |
Example
for await (const event of getClient().stream('sendMessage', {}, { text: 'Hello' })) {
console.log(event.json ?? event.data);
}
TypeScript declaration
stream<K extends Id<Ops>>(op: K, params: Ops[K]['params'], body?: Ops[K]['body'], options?: { signal?: AbortSignal }): AsyncGenerator<SseEvent>;client.invalidateAll()
Views on screen fetch again.
- Returns
void
Example
getClient().invalidateAll();
TypeScript declaration
invalidateAll(): void;client.reset()
For a logout or another user: requests in flight are cancelled, cached queries and entities are dropped, and what a mounted view shows is fetched again. Mutations already sent are not cancelled.
- Returns
void
Example
configure({ api: operations, onUnauthorized: () => { getClient().reset(); router.navigate('/login'); } });
TypeScript declaration
reset(): void;derived(({ all, get }) => value, client?)
all('Order') lists every loaded entity of a type, get(name, id) returns one. It recomputes when they change and never makes a request: fetching is declared on a view.
- Returns
Signal
Example
import { derived } from '@xoong/data';
const openOrders = derived(({ all }) => all('Order').filter((o) => o.status === 'open').length);
TypeScript declaration
function derived<T>(fn: (ctx: { all(name: string): any[]; get(name: string, id: string | number): any }) => T, client?: Client<any>): Signal<T>;connectStream({ client, url, EventSource?, onStatus?, onError? })
The events are patch, upsert, delete and resync. configure({ streamUrl }) does this for you when a view has live.
- Returns
{ close() }
Example
import { connectStream, getClient } from '@xoong/data';
const stream = connectStream({ client: getClient(), url: '/api/events' });
TypeScript declaration
function connectStream(options: { client: Client<any>; url: string; EventSource?: typeof EventSource; onStatus?: (s: 'open' | 'error' | 'closed') => void; onError?: (e: Error) => void }): { close(): void };class XoongError extends Error
Built from problem+json or { error: { code, message } }. Fields: status, title, detail, code, retryAfterS, operation, and fields, the messages by field ({ email: 'is not valid' }).
Example
document.addEventListener('xo-error', (event) => console.warn(event.detail.status, event.detail.detail));
TypeScript declaration
class XoongError extends Error {
status: number;
title?: string;
detail?: string;
code?: string;
retryAfterS?: number;
problem?: any;
operation?: string;
/** Per-field messages when the server reports them (`{ email: 'is not valid' }`). */
fields: Record<string, string>;
}setFormat(name, fn)
Then {price | currency} works in any view. The function receives the value and the active language. A format that throws shows the plain value.
- Returns
void
Parameters
| Name | Type | What it is |
|---|---|---|
name | string | The name used after |. |
fn | function | (value, locale) => string. |
Example
import { setFormat } from '@xoong/data/view';
setFormat('currency', (value, locale) => new Intl.NumberFormat(locale, { style: 'currency', currency: 'EUR' }).format(value));
TypeScript declaration
export function setFormat(name: string, fn: (value: any, locale?: string) => string): voidGuide Recipes
confirm(message, options?)
The built-in dialog, a native modal. Without a kind the confirming button is red, as for something that cannot be undone. Escape answers false.
- Returns
Promise<boolean>
Parameters
| Name | Type | What it is |
|---|---|---|
title | string | A heading above the message. |
kind | string | info, success, warning or danger: an icon and the matching colour. |
detail | string | A second, quieter line. |
ok | string | The text of the confirming button. |
cancel | string | The text of the cancelling button. |
Example
import { confirm } from '@xoong/ui';
if (await confirm('Delete 3 items?', { kind: 'danger', ok: 'Delete' })) remove();
TypeScript declaration
function confirm(message: string, options?: DialogLook & { ok?: string; cancel?: string }): Promise<boolean>;Guide Dialogs and modals
alert(message, options?)
One button; resolves when the dialog is closed. kind defaults to info.
- Returns
Promise<void>
Parameters
| Name | Type | What it is |
|---|---|---|
title | string | A heading above the message. |
kind | string | info, success, warning or danger: an icon and the matching colour. |
detail | string | A second, quieter line. |
ok | string | The text of the button. |
Example
await alert('Saved', { kind: 'success' });
TypeScript declaration
function alert(message: string, options?: DialogLook & { ok?: string }): Promise<void>;Guide Dialogs and modals
prompt(message, options?)
Resolves with the text, or null when cancelled. The field is native, so required, pattern and type are checked by the browser.
- Returns
Promise<string | null>
Parameters
| Name | Type | What it is |
|---|---|---|
title | string | A heading above the message. |
kind | string | info, success, warning or danger: an icon and the matching colour. |
detail | string | A second, quieter line. |
value | string | The starting text. |
label | string | The accessible name of the field. |
placeholder | string | Hint text of the empty field. |
type | string | The input type. |
required | boolean | The field must be filled. |
pattern | string | A pattern the text must match. |
ok | string | The text of the confirming button. |
cancel | string | The text of the cancelling button. |
Example
const name = await prompt('Name this view', { value: 'My view', required: true });
TypeScript declaration
function prompt(message: string, options?: DialogLook & { value?: string; label?: string; placeholder?: string; type?: string; required?: boolean; pattern?: string; ok?: string; cancel?: string }): Promise<string | null>;Guide Dialogs and modals
ask(message, { buttons, ...look })
Resolves with the value of the button pressed; Escape answers with the value of the one marked cancel, or null when none is.
- Returns
Promise<unknown>
Parameters
| Name | Type | What it is |
|---|---|---|
buttons | array | Required. Each button is { label, value?, primary?, danger?, cancel? }. |
title | string | A heading above the message. |
kind | string | info, success, warning or danger: an icon and the matching colour. |
detail | string | A second, quieter line. |
Example
const answer = await ask('Save changes?', {
buttons: [{ label: 'Discard', value: 'no' }, { label: 'Save', value: 'yes', primary: true }],
});
TypeScript declaration
function ask(message: string, options: DialogLook & { buttons: DialogChoice[] }): Promise<unknown>;Guide Dialogs and modals
confirmLabels.ok, confirmLabels.cancel
Replace them for another wording or another language: confirmLabels.ok = 'Delete'.
Example
import { confirmLabels } from '@xoong/ui';
confirmLabels.ok = 'Yes, delete';
TypeScript declaration
const confirmLabels: { ok: string; cancel: string };Guide Dialogs and modals
setConfirm(fn)
For xo-confirm and the question asked when a form with unsaved changes is left. fn(message, context) returns true (or a promise of it) to go on.
- Returns
void
Example
import { setConfirm } from '@xoong/ui';
setConfirm((message) => myDialog.ask(message));
TypeScript declaration
function setConfirm(fn: (message: string, context?: ConfirmContext) => boolean | Promise<boolean>): void;Guide Dialogs and modals
toast.show({ message, kind?, action?, timeout? })
A message that asks nothing and may offer one action, for example Undo. It is the same notice xo-notice shows.
- Returns
void
Parameters
| Name | Type | What it is |
|---|---|---|
message | string | The text. |
kind | string | success or info (the default). |
action | object | { label, run }: a button, for example Undo. |
timeout | number | Seconds before it hides: 6 unless <xo-toast timeout> says otherwise; 0 keeps it until it is dismissed. |
Example
import { toast } from '@xoong/ui';
toast.show({ message: 'Published 3', action: { label: 'Undo', run: undo } });
TypeScript declaration
show(notice: { message: string; kind?: string; action?: { label: string; run: () => unknown }; timeout?: number }): void;Guide Dialogs and modals
router.guard((to, from) => result)
Return true (or nothing) to continue, false to cancel, or a path to redirect there. Returns a function that removes the guard.
- Returns
function
Example
const stop = router.guard((to) => (loggedIn() || to.path === '/login' ? true : '/login'));
TypeScript declaration
guard(fn: (to: RouteLocation, from: RouteLocation | null) => GuardResult | Promise<GuardResult>): () => void;router.current
{ path, params, query, url, route }, or null before the first navigation.
Example
console.log(router.current?.params.id);
TypeScript declaration
readonly current: RouteLocation | null;configureI18n({ locale?, fallback?, messages?, load? })
Resolves when the catalogs of the language are loaded.
- Returns
Promise<void>
Parameters
| Name | Type | What it is |
|---|---|---|
locale | string | The active language. |
fallback | string | The language to use for a missing message. |
messages | object | Catalogs by language: { vi: { key: 'message' } }. |
load | function | (code) => catalog: loads a catalog on demand. |
Example
import { configureI18n } from '@xoong/i18n';
configureI18n({ locale: 'en', fallback: 'en', load: (code) => import(`/i18n/${code}.json`) });
TypeScript declaration
function configureI18n(options?: { locale?: string; fallback?: string; messages?: Record<string, Record<string, string>>; load?: (code: string) => Promise<any> | any }): void;Guide Recipes
setLocale(code)
Loads its catalog first, so the page switches once, then updates <html lang dir>. The last call wins.
- Returns
Promise<void>
Example
await setLocale('vi');
TypeScript declaration
function setLocale(code: string): Promise<void>;Guide Recipes
t(key, args?, fallbackText?)
The message in the active language; fallbackText is shown when the key is unknown, otherwise the key.
- Returns
string
Parameters
| Name | Type | What it is |
|---|---|---|
key | string | The message key. |
args | object | Values for {placeholders} and plural forms. |
fallbackText | string | Shown when no language has the key. |
Example
t('articles.count', { count: 3 });
TypeScript declaration
function t(key: string, args?: Record<string, any>, fallbackText?: string): string;Guide Recipes
locale.value
Read it inside an effect or a template; set it with setLocale.
Example
effect(() => console.log('language:', locale.value));
TypeScript declaration
const locale: Signal<string>;addMessages(code, messages)
Merged over what is there.
- Returns
void
Example
addMessages('vi', { 'home.title': 'Chào mừng' });
TypeScript declaration
function addMessages(code: string, messages: Record<string, string>): void;onLocaleChange(fn)
Not for the current language. Returns a function that stops it.
- Returns
function
Example
const stop = onLocaleChange((code) => console.log('now', code));
TypeScript declaration
function onLocaleChange(fn: (code: string) => void): () => void;detectLocale(supported, { default?, query?, storageKey? }?)
The one in ?lang=, a saved choice, then the browser's preferences, then default (the first supported one).
- Returns
string
Example
const code = detectLocale(['en', 'vi']);
TypeScript declaration
function detectLocale(supported: string[], options?: { default?: string; query?: string; storageKey?: string }): string;signal(value)
.value reads (tracked inside an effect, a computed or a template function) and writes; .peek() reads without tracking.
- Returns
Signal
Example
import { signal } from '@xoong/core';
const count = signal(0);
count.value++;
TypeScript declaration
function signal<T>(value: T): Signal<T>;computed(fn)
It recomputes lazily, when something it read has changed.
- Returns
ReadonlySignal
Example
const double = computed(() => count.value * 2);
TypeScript declaration
function computed<T>(fn: () => T): ReadonlySignal<T>;effect(fn)
fn may return a cleanup function. For side effects only: never fetch data in an effect, declare the operation on a view. Returns a function that stops it.
- Returns
function
Example
const stop = effect(() => { document.title = `${count.value} items`; });
TypeScript declaration
function effect(fn: () => void | (() => void)): () => void;batch(fn)
The effects run once, after the outermost batch ends.
- Returns
what fn returns
Example
batch(() => { first.value = 'Ada'; last.value = 'Lovelace'; });
TypeScript declaration
function batch<T>(fn: () => T): T;untracked(fn)
Inside fn a read is not tracked by the running effect or computed.
- Returns
what fn returns
Example
effect(() => { const n = untracked(() => count.value); });
TypeScript declaration
function untracked<T>(fn: () => T): T;html`...`
A function in a child or attribute position is reactive. name=${v} sets an attribute, ?name=${v} a boolean attribute, .name=${v} a property, @event=${fn} a listener and style:--x=${v} a CSS custom property.
- Returns
TemplateResult
Example
html`<button class=${() => cls.value} @click=${save}>${() => label.value}</button>`
TypeScript declaration
function html(strings: TemplateStringsArray, ...values: unknown[]): TemplateResult;render(result, container)
Replaces the children of container. Returns the dispose function.
- Returns
function
Example
const dispose = render(html`<p>${() => count.value}</p>`, document.body);
TypeScript declaration
function render(result: TemplateResult, container: Element | DocumentFragment): () => void;class XoElement extends HTMLElement
static props declares attributes that are also properties and signals. Implement render() when the element owns its content, or connected() to enhance markup that is already there.
Example
class UserCard extends XoElement {
static props = { name: String, age: Number };
render() { return html`<h3>${() => this.name}</h3>`; }
}
define('user-card', UserCard);
TypeScript declaration
class XoElement extends HTMLElement {
/** Attributes <-> signals. The attribute is the kebab-case of the name (`maxSize` is `max-size`). */
static props: Record<string, XoPropType>;
static get observedAttributes(): string[];
constructor();
/** The signal behind a prop (reading `this.name` already tracks it). */
_sig(name: string): Signal<any>;
attributeChangedCallback(attr: string, oldValue: string | null, value: string | null): void;
connectedCallback(): void;
disconnectedCallback(): void;
/**
* Implement it only if the element OWNS its content: the result is rendered into the element when it connects.
* To enhance server-rendered HTML in place, implement `connected()` instead.
*/
render?(): TemplateResult | null | undefined | void;
/** Runs after `render()` when the element is connected. */
connected?(): void;
/** Runs after the element's effects and rendering were disposed, when it is disconnected. */
disconnected?(): void;
/** Resolves with the element when its children are there: at once once the document is parsed, else after the parser reached the end. Await it in `connected()`. */
ready(): Promise<this>;
/** A side effect bound to this element's lifetime: disposed on disconnect. */
effect(fn: () => void | (() => void)): void;
/** Dispatch a bubbling, composed CustomEvent. */
emit<T = unknown>(name: string, detail?: T): void;
}Guide Your own control
define(tag, class)
A tag that is already defined is left alone. The tag must contain a hyphen.
- Returns
void
Example
define('user-card', UserCard);
TypeScript declaration
function define(tag: string, cls: typeof XoElement): void;Guide Your own control
class XoControl extends XoElement
Gives the element name, required, disabled and readonly, a value, validation and a place in the form model. Every field control of @xoong/controls extends it.
Example
class RatingInput extends XoControl { static props = { ...XoControl.props, max: Number }; }
TypeScript declaration
class XoControl extends XoElement {
static formAssociated: boolean;
static props: { name: StringConstructor; required: BooleanConstructor; disabled: BooleanConstructor; readonly: BooleanConstructor };
constructor();
/** The form field name. */
name: string;
required: boolean;
disabled: boolean;
readonly: boolean;
// ---- value
/** The value. A string by default; a subclass may hold anything and override `serialize`. Reading it is tracked. */
get value(): any;
set value(v: any);
/** Set the value programmatically (like `input.value = x`: no events). Before the control has started, it also becomes the initial value. */
setValue(v: any): void;
/** The person changed the value: sets it and fires `input`. */
userInput(v: any): void;
/** The person finished a change: fires `change`. */
userChange(): void;
/** What the form submits for a value: a string, a File, FormData, or null for nothing. Override for structured values. */
serialize(v: any): string | File | FormData | null;
/** Whether a value counts as "nothing entered" for `required`. Override for structured values. */
isEmpty(v: any): boolean;
/** Validation hook: return an error message for an invalid value, a falsy value when it is valid. Runs on every change. */
validate(value: any): string | null | undefined | false;
// ---- state (reading these inside a template or an effect is tracked)
/** The value the control started with (what `reset` goes back to and `dirty` compares with). */
get initialValue(): any;
get dirty(): boolean;
get pristine(): boolean;
get touched(): boolean;
/** True when there is no validation error. */
get valid(): boolean;
get invalid(): boolean;
/** Mark the control as visited (blur does this). */
markTouched(): void;
// ---- validation
/** The message to show, or '' when valid. */
get validationMessage(): string;
/** Disabled by its own attribute or by a disabled fieldset. */
get isDisabled(): boolean;
get willValidate(): boolean;
get validity(): XoValidity;
/** Set your own error (cross-field rules, server errors). An empty string clears it. */
setCustomValidity(message: string | null | undefined): void;
/** True when valid (or not validated); otherwise fires a cancelable `invalid` event and returns false. */
checkValidity(): boolean;
/** Like `checkValidity`, and on failure marks the control touched and focuses `focusTarget`. */
reportValidity(): boolean;
/** The element inside that takes focus (and anchors the validation bubble). */
get focusTarget(): HTMLElement | null;
// ---- reset
/** Back to the initial value, untouched. The form calls this on reset. */
reset(): void;
/** Make the current value the new baseline, as after a successful save: `dirty` becomes false. */
markPristine(): void;
// ---- form-associated callbacks
formResetCallback(): void;
formDisabledCallback(disabled: boolean): void;
formStateRestoreCallback(state: any): void;
/** The form this control belongs to (through ElementInternals, else the closest `<form>`). */
get form(): HTMLFormElement | null;
}Guide Your own control
createForm({ schema?, value?, rules?, serialize? })
Holds the value, the errors and the dirty state by path. <form xo-submit> creates one for you; use this in tests and for a form that is not in a view.
- Returns
FormModel
Parameters
| Name | Type | What it is |
|---|---|---|
schema | object | A request schema node. |
value | object | The starting record. |
rules | object | Rules by field path; * gets the whole record. |
serialize | function | (draft, body) => body: change what is sent. |
Example
import { createForm } from '@xoong/forms';
const form = createForm({ rules: { slug: (v) => (/^[a-z-]+$/.test(v) ? '' : 'Lower case letters only') } });
form.set('slug', 'My Page');
TypeScript declaration
function createForm(options?: { schema?: SchemaNode; value?: any; rules?: Rules; serialize?: (draft: any, body: any) => any }): FormModel;Guide Forms
attach(form, { schema, record?, hadRecord? })
The binder that <form xo-submit> uses, for a form you handle yourself.
- Returns
{ model, submit, detach, rescan }
Example
const { model } = attach(document.querySelector('form'), { schema });
TypeScript declaration
function attach(form: HTMLFormElement, options: { schema: SchemaNode; record?: () => any; hadRecord?: boolean }): { model: FormModel; submit(send: (body: any) => Promise<any>): Promise<any>; detach(): void; rescan(): void };Guide Forms
setMessages({ code: (params) => message })
For another language or your own wording. Each message receives its parameters, for example { min: 3 }. The codes are required, type, minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf, minLength, maxLength, pattern, format, enum, minItems, maxItems, uniqueItems and variant. resetMessages() brings the English ones back.
- Returns
void
Example
import { setMessages } from '@xoong/forms';
setMessages({ required: () => 'Bắt buộc', minLength: ({ min }) => `Ít nhất ${min} ký tự` });
TypeScript declaration
function setMessages(partial: Record<string, (params: Record<string, any>) => string>): void;Guide Forms
sanitizeHtml(html, { images? }?)
Keeps p, h2, h3, strong, em, u, ul, ol, li, blockquote, br, a (safe links only) and img (safe sources only). images: false removes <img>. It needs a DOM.
- Returns
string
Example
import { sanitizeHtml } from '@xoong/richtext';
const clean = sanitizeHtml(untrusted, { images: false });
TypeScript declaration
function sanitizeHtml(html: string, options?: { images?: boolean }): string;Guide Rich text
Template syntax
What goes between braces inside an <xo-view>: paths, formats and view variables. 11 entries.
{path}{path | format}$loading, $refreshing$error$index, $n$hasPrev, $hasNext, $from, $to$selectedCount, $selected$stream, $streaming$formp-<name>hidden="{flag}"
{path}
A path reads a field of the current item (. is the item itself, a.b is nested, a[0].b indexes) and then the view's p-* parameters. The text is escaped. A javascript: URL becomes about:blank, and on* attributes and srcdoc are not interpolated. {{name}} and \{name} stay as written, and braces around anything that is not a path (JSON, CSS) are left alone.
Example
<xo-view get="getPerson" p-id="1">
<h2>{name}</h2>
<a href="mailto:{email}">{email}</a>
</xo-view>
Guide Data and tables
{path | format}
The formats are date, datetime, time, relative, number, json and localized. They use Intl and the page language. A value that cannot be formatted is shown as it is. Add your own with setFormat(). An unknown name is reported once on the console and the plain value is shown.
| Format | Shows |
|---|---|
date | A calendar date in the page language: Nov 25, 2026. |
datetime | Date and time. |
time | Time of day. |
relative | "3 days ago", "tomorrow". |
number | A number with the language's separators. |
json | The value as JSON text. |
localized | A value that is an object by language ({ en, vi }): the active language, then its base language, then English, then any filled one. |
Example
<xo-view get="listPeople">
<p xo-each="items">{name}: {createdAt | relative}, {total | number}</p>
</xo-view>
Guide Data and tables
$loading, $refreshing
$loading is true while a request runs and there is nothing to show yet. $refreshing is true while a request runs and the previous result is still on screen (a sort or a page loads), so the view does not collapse. The view also carries data-refreshing for CSS.
Example
<xo-view get="listPeople">
<p xo-if="$loading">Loading…</p>
<p xo-if="$refreshing">Updating…</p>
</xo-view>
Guide Loading, empty and error
$error
A XoongError with status, code and detail, or nothing when the load worked.
Example
<xo-view get="listPeople">
<p xo-if="$error">{$error.code}: {$error.detail}</p>
</xo-view>
Guide Loading, empty and error
$index, $n
$index counts from 0 and $n from 1. In nested xo-each loops $i0 (the outer one), $i1, ... name the position at each depth.
Example
<li xo-each="items">{$n}. {name}</li>
Guide Data and tables
$hasPrev, $hasNext, $from, $to
For lists that take limit and offset and answer with total. $from and $to are the numbers of the first and the last row shown (the "1–12 of 48" text).
Example
<xo-view get="listPeople" p-limit="10">
<button xo-param="offset-=10" xo-if="$hasPrev">Previous</button>
<span>{$from}–{$to}</span>
<button xo-param="offset+=10" xo-if="$hasNext">Next</button>
</xo-view>
Guide Data and tables
$selectedCount, $selected
$selectedCount is the number of ticked rows (xo-select), $selected the rows themselves, for xo-body="$selected".
Example
<button xo-click="deletePerson" xo-selected xo-if="$selectedCount">Delete {$selectedCount}</button>
Guide Data and tables
$stream, $streaming
$stream holds the events of the latest streamed call (an operation that answers with server-sent events) and $streaming whether it still runs.
Example
<p xo-if="$streaming">Receiving…</p>
$form
Inside a <form xo-submit>: $form.dirty, $form.valid, $form.submitting, $form.stale, and by field path $form.errors['title.vi'], $form.touched[...], $form.filled[...].
Example
<xo-view get="getPerson" p-id="1">
<form xo-submit="updatePerson">
<input name="name" aria-label="Name">
<span xo-if="$form.dirty">Unsaved changes</span>
<button>Save</button>
</form>
</xo-view>
Guide Forms
p-<name>
On <xo-view>: p-id="42" is the path parameter id. p-id="?id" reads ?id= from the page URL and p-id=":id" reads a route parameter. Inside the view {id} reads it.
Example
<xo-view get="getPerson" p-id="?id"><h1>{name}</h1></xo-view>
Guide Data and tables
hidden="{flag}"
When {flag} is the whole value of hidden, disabled, checked, selected, readonly, required, open, multiple, inert, autofocus, controls, loop, muted or reversed, the attribute is present while the value is true (an empty list is false).
Example
<button disabled="{$loading}" xo-refresh>Refresh</button>
Guide Data and tables
Nothing here matches. Try a shorter word.