UI componentsdocs/ui/components/selection.md

Selection

Choose from options: checkboxes, radios, selects, chips, toggles and sliders.

← Component reference

Checkbox #

Single checkboxes and checkbox groups.

Checkbox #

Checkbox. The native <input> is the host: class, style and forwarded attributes (id, name, required, aria-*) land on it; classes.root styles the label row.

function Example() {
    const updates = signal(true);
    return (
        <Checkbox
            label="Email me about product updates"
            checked={updates()}
            onChange={(e) => updates.set((e.target as HTMLInputElement).checked)}
        />
    );
}

Slots: root input label

PropTypeRequiredDefaultDescription
checkedbooleanWhether it is checked (controlled).
disabledbooleanDisables the checkbox.
invalidbooleanMarks the value invalid (aria-invalid and error styling).
labelcontentLabel text or content, clickable to toggle.
namestringField name submitted with the form.
onChange(e: Event) => voidCalled on toggle; read e.target.checked.
valuestringNative value submitted with the form.

CheckboxGroup #

Multiple-choice checkboxes in a fieldset.

Regions
const regions = [
    { value: "fra1", label: "Frankfurt" },
    { value: "iad1", label: "Washington, D.C." },
    { value: "hnd1", label: "Tokyo" },
];

function Example() {
    const selected = signal(["fra1"]);
    return (
        <CheckboxGroup
            legend="Regions"
            options={regions}
            value={selected()}
            onChange={selected.set}
        />
    );
}

Slots: root legend option

PropTypeRequiredDefaultDescription
onChange(value: string[]) => voidyesCalled with the new list of checked values.
options{ value: string; label: unknown; disabled?: boolean; }[]yesOptions: value, label, optional disabled.
valuestring[]yesValues of the checked options (controlled).
disabledbooleanDisables every option.
legendcontentVisible group label rendered as <legend>.

Checklist #

Checklist of items with checkboxes.

2 of 3 done

function Example() {
    const items = signal<ChecklistItemData[]>([
        { id: "domain", label: "Connect a domain", done: true },
        { id: "invite", label: "Invite your team", done: true },
        { id: "deploy", label: "Ship your first deploy" },
    ]);
    const done = () => items().filter((i) => i.done).length;
    return (
        <Stack gap="0.5rem">
            <Checklist items={items()} onChange={items.set} />
            <Text muted>
                {done()} of {items().length} done
            </Text>
        </Stack>
    );
}

Slots: root checkbox item label

PropTypeRequiredDefaultDescription
itemsChecklistItemData[]yesThe checklist items (controlled).
onChange(items: ChecklistItemData[]) => voidyesCalled with the updated items after a tick changes.

ChecklistItemData

FieldTypeRequiredDescription
idstringyesItem id.
labelstringyesItem text.
donebooleanWhether the item is ticked.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

RadioGroup #

Radio group (role="radiogroup"). Forwarded attributes land on the group.

const plans = [
    { value: "free", label: "Free" },
    { value: "pro", label: "Pro" },
    { value: "team", label: "Team" },
];

function Example() {
    const plan = signal("pro");
    return (
        <RadioGroup
            label="Plan"
            name="plan"
            options={plans}
            value={plan()}
            onChange={(e) => plan.set((e.target as HTMLInputElement).value)}
        />
    );
}

Slots: root input label option

PropTypeRequiredDefaultDescription
namestringyesShared name of the radio inputs (also the form field name).
optionsRadioOption[]yesThe options, in order.
disabledbooleanDisables every option.
inlinebooleanLay options out in a row.
labelstringAccessible name for the group.
onChange(e: Event) => voidCalled when the selection changes; read e.target.value.
valuestringSelected option's value (controlled).

RadioOption

FieldTypeRequiredDescription
valuestringyesOption value (what value / onChange use).
labelcontentyesText or content shown for the option.
disabledbooleanShown but can't be chosen.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Switch #

Toggle switch (role="switch"). Like {@link Checkbox}, the <input> (the visible track) is the host; classes.root styles the label row.

function Example() {
    const previews = signal(true);
    return (
        <Switch
            label="Preview deploys"
            checked={previews()}
            onChange={(e) => previews.set((e.target as HTMLInputElement).checked)}
        />
    );
}

Slots: root input label

PropTypeRequiredDefaultDescription
checkedbooleanWhether it is on (controlled).
disabledbooleanDisables the switch.
labelcontentLabel text or content, clickable to toggle.
namestringField name submitted with the form.
onChange(e: Event) => voidCalled on toggle; read e.target.checked.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Select #

Pick one or many options from a list.

Select #

Native select; forwarded attributes land on <select>.

const plans = [
    { value: "free", label: "Free" },
    { value: "pro", label: "Pro" },
    { value: "team", label: "Team" },
];

function Example() {
    const plan = signal("pro");
    return (
        <Select
            aria-label="Plan"
            options={plans}
            value={plan()}
            onChange={(e) => plan.set((e.target as HTMLInputElement).value)}
        />
    );
}
PropTypeRequiredDefaultDescription
optionsSelectOption[]yesThe options, in order.
disabledbooleanDisables the select.
invalidbooleanMarks the value invalid (aria-invalid and error styling).
namestringField name submitted with the form.
onChange(e: Event) => voidCalled when the selection changes; read e.target.value.
placeholderstringEmpty first option shown while nothing is selected.
valuestring""Selected option's value (controlled).

SelectOption

FieldTypeRequiredDescription
valuestringyesOption value (what value / onChange use).
labelstringyesText shown for the option.
disabledbooleanShown but can't be chosen.

NativeSelect #

Native <select> with a value callback.

const regions = [
    { value: "fra1", label: "Frankfurt" },
    { value: "iad1", label: "Washington, D.C." },
    { value: "hnd1", label: "Tokyo" },
];

function Example() {
    const region = signal("fra1");
    return (
        <NativeSelect
            aria-label="Region"
            options={regions}
            value={region()}
            onChange={region.set}
        />
    );
}
PropTypeRequiredDefaultDescription
onChange(value: string) => voidyesCalled with the selected value.
options{ value: string; label: string; disabled?: boolean; }[]yesOptions: value, label, optional disabled.
disabledbooleanDisables the select.
invalidbooleanMarks the value invalid (aria-invalid and error styling).
namestringField name submitted with the form.
placeholderstringEmpty first option shown while nothing is selected.
valuestring""Selected option's value (controlled).

MultiSelect #

Multi-choice listbox dropdown.

const regions = [
    { value: "fra1", label: "Frankfurt" },
    { value: "iad1", label: "Washington, D.C." },
    { value: "hnd1", label: "Tokyo" },
];

function Example() {
    const selected = signal(["fra1", "iad1"]);
    return (
        <MultiSelect
            aria-label="Regions"
            options={regions}
            value={selected()}
            onChange={selected.set}
        />
    );
}

Slots: root control menu option

PropTypeRequiredDefaultDescription
onChange(value: string[]) => voidyesCalled with the new list of selected values.
options{ value: string; label: string; disabled?: boolean; }[]yesOptions: value, label, optional disabled.
valuestring[]yesValues of the selected options (controlled).
disabledbooleanDisables the control.
placeholderstring"Select…"Trigger text while nothing is selected.

Autocomplete #

Combobox with suggestion list (↑ ↓ Enter Escape, aria-activedescendant). The <input> is the host; classes.root styles the wrapper.

function Example() {
    const country = signal("");
    return (
        <Autocomplete
            aria-label="Country"
            placeholder="Start typing a country"
            value={country()}
            options={["Germany", "Japan", "United Kingdom", "United States"]}
            onChange={country.set}
        />
    );
}

Slots: root input menu option

PropTypeRequiredDefaultDescription
onChange(value: string) => voidyesCalled with the new text as the user types or picks a suggestion.
optionsstring[]yesSuggestions, filtered by the typed text.
valuestringyesCurrent text (controlled).
disabledbooleanDisables the field.
invalidbooleanMarks the value invalid (aria-invalid and error styling).
limitnumber8Max suggestions shown (default 8).
namestringField name submitted with the form.
placeholderstringHint shown while empty.
renderOption(option: string) => unknownRender an option (default: the text).

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

ChoiceCard #

Selectable card wrapping a native radio/checkbox.

function Example() {
    const plan = signal("pro");
    return (
        <Group gap="0.75rem">
            <ChoiceCard
                type="radio"
                name="plan"
                value="free"
                checked={plan() === "free"}
                label="Free"
                description="For side projects"
                onChange={(checked) => checked && plan.set("free")}
            />
            <ChoiceCard
                type="radio"
                name="plan"
                value="pro"
                checked={plan() === "pro"}
                label="Pro"
                description="$20 per seat / month"
                onChange={(checked) => checked && plan.set("pro")}
            />
        </Group>
    );
}

Slots: root body description input label

PropTypeRequiredDefaultDescription
checkedbooleanyesWhether this card is selected (controlled).
labelcontentyesCard title.
onChange(checked: boolean) => voidyesCalled with the new checked state.
descriptioncontentText under the title.
disabledbooleanDisables the card.
namestringInput name; cards with the same name form a radio group.
type"checkbox" | "radio""radio"radio for one-of-many cards (share a name), checkbox for independent ones.
valuestringValue submitted with the form.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Chips #

Toggleable chips.

Chip #

Toggleable pill (aria-pressed).

function Example() {
    const on = signal(true);
    return (
        <Chip checked={on()} onChange={on.set}>
            TypeScript
        </Chip>
    );
}
PropTypeRequiredDefaultDescription
checkedbooleanWhether the chip is selected (controlled).
childrencontentChip label.
disabledbooleanDisables the chip.
iconcontentLeading icon.
onChange(checked: boolean) => voidCalled with the new checked state.

ChipGroup #

Single or multiple choice chips.

Frameworks
function Example() {
    const frameworks = signal(["arachne"]);
    return (
        <ChipGroup
            legend="Frameworks"
            multiple
            options={[
                { value: "arachne", label: "Arachne" },
                { value: "solid", label: "Solid" },
                { value: "svelte", label: "Svelte" },
            ]}
            value={frameworks()}
            onChange={frameworks.set}
        />
    );
}

Slots: root chip legend

PropTypeRequiredDefaultDescription
onChange(value: string[]) => voidyesCalled with the new list of selected values.
options{ value: string; label: unknown; disabled?: boolean; }[]yesChips: value, label, optional disabled.
valuestring[]yesValues of the selected chips (controlled).
disabledbooleanDisables every chip.
legendcontentGroup caption (the group's accessible name).
multiplebooleanAllow several chips selected; otherwise picking one replaces the other.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Segmented & toggle group #

Pick one (or several) of a few options.

Segmented #

Pressed-button group.

function Example() {
    const range = signal("7d");
    return (
        <Segmented
            label="Range"
            items={[
                { id: "24h", label: "24h" },
                { id: "7d", label: "7 days" },
                { id: "30d", label: "30 days" },
            ]}
            value={range()}
            onChange={range.set}
        />
    );
}

Slots: root segment

PropTypeRequiredDefaultDescription
items{ id: string; label: string; disabled?: boolean; }[]yesOptions: id, label and optional disabled.
onChange(id: string) => voidyesCalled with the id the user picks.
valuestringyesId of the selected option.
labelstring"Options"Accessible name for the group (default "Options").

ToggleGroup #

Pressed-toggle group.

function Example() {
    const formats = signal<string[]>(["bold"]);
    return (
        <ToggleGroup
            label="Formatting"
            multiple
            items={[
                { id: "bold", label: "Bold" },
                { id: "italic", label: "Italic" },
                { id: "underline", label: "Underline" },
            ]}
            value={formats()}
            onChange={(next) => formats.set(Array.isArray(next) ? next : next ? [next] : [])}
        />
    );
}

Slots: root item

PropTypeRequiredDefaultDescription
itemsToggleItem[]yesThe options, in order.
onChange(value: string | string[] | null) => voidyesCalled with the new pressed id(s) (null when the pressed single option is released).
valuestring | string[]yesPressed id (single), ids (multiple), or null for none.
labelstringAccessible name for the group.
multiplebooleanAllow several options pressed at once (value is an array).

ToggleItem

FieldTypeRequiredDescription
idstringyesOption id, passed to onChange and matched against value.
labelcontentyesOption text or content.
disabledbooleanShown but can't be toggled.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Sliders #

Pick a value, a range or an angle.

Slider #

Range input; forwarded attributes land on <input type="range">.

Volume: 60

function Example() {
    const volume = signal(60);
    return (
        <>
            <Slider aria-label="Volume" value={volume()} onChange={volume.set} />
            <Text muted>Volume: {volume()}</Text>
        </>
    );
}
PropTypeRequiredDefaultDescription
onChange(value: number) => voidyesCalled with the new value while dragging or using the keyboard.
valuenumberyesCurrent value (controlled).
disabledbooleanDisables the slider.
maxnumber100Highest value.
minnumber0Lowest value.
stepnumber1Increment between values (also for arrow keys).

RangeSlider #

Two-thumb range.

$40 – $120

function Example() {
    const range = signal<[number, number]>([40, 120]);
    return (
        <>
            <RangeSlider
                aria-label="Price range"
                min={0}
                max={200}
                value={range()}
                onChange={range.set}
            />
            <Text muted>
                ${range()[0]} – ${range()[1]}
            </Text>
        </>
    );
}

Slots: root end start

PropTypeRequiredDefaultDescription
onChange(value: [number, number]) => voidyesCalled with the new [start, end]; handles can't cross.
value[number, number]yesStart and end of the range (controlled).
disabledbooleanDisables both handles.
endLabelstring"Range end"Accessible name of the end handle.
maxnumber100Highest value.
minnumber0Lowest value.
startLabelstring"Range start"Accessible name of the start handle.
stepnumber1Increment between values.

AngleSlider #

Circular slider for an angle.

135°

135°

function Example() {
    const angle = signal(135);
    return (
        <Group gap="0.75rem">
            <AngleSlider value={angle()} onChange={angle.set} />
            <Text muted>{angle()}°</Text>
        </Group>
    );
}

Slots: root arm hub knob label svg track

PropTypeRequiredDefaultDescription
onChange(deg: number) => voidyesCalled with the new angle while dragging or using the arrow keys.
valuenumberyesAngle in degrees, 0–359 (controlled).
labelstring"Angle"Accessible name (default "Angle").
sizenumber120Diameter in pixels.
stepnumber5Degrees per arrow key press (default 5).

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Rating #

Star rating (radio pattern with roving tabindex, arrow keys).

function Example() {
    const stars = signal(4);
    return <Rating aria-label="Rating" value={stars()} onChange={stars.set} />;
}

Slots: root star

PropTypeRequiredDefaultDescription
onChange(value: number) => voidyesCalled with the picked rating.
valuenumberyesCurrent rating (controlled; 0 for none).
countnumber5Number of stars.
disabledbooleanRead-only display.
starLabel(n: number) => stringAccessible label per star, e.g. (n) => \${n} of 5``.
symbolcontent"★"Star glyph (default ★).

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

TransferList #

Dual-list mover (Mantine TransferList). Rows keep their DOM between moves.

Available

Selected

function Example() {
    const lists = signal({ left: ["Frankfurt", "Tokyo", "São Paulo"], right: ["Washington, D.C."] });
    return (
        <TransferList
            leftTitle="Available"
            rightTitle="Selected"
            left={lists().left}
            right={lists().right}
            onChange={lists.set}
        />
    );
}

Slots: root actions item list pane title

PropTypeRequiredDefaultDescription
leftstring[]yesItems in the left list.
onChange(next: { left: string[]; right: string[]; }) => voidyesCalled with both lists after items are moved.
rightstring[]yesItems in the right list.
leftTitlestring"Available"Heading of the left list.
rightTitlestring"Selected"Heading of the right list.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Preference toggles #

View, density, theme and language switches.

ViewToggle #

List / grid toggle.

Showing a list

function Example() {
    const view = signal<"list" | "grid">("list");
    return (
        <Group gap="0.75rem">
            <ViewToggle value={view()} onChange={view.set} />
            <Text muted>Showing a {view()}</Text>
        </Group>
    );
}

Slots: root option

PropTypeRequiredDefaultDescription
onChange(value: "grid" | "list") => voidyesCalled with the view the user picks.
value"grid" | "list"yesCurrent view.

DensityToggle #

Compact / comfortable density switch.

Density: compact

function Example() {
    const density = signal<Density>("compact");
    return (
        <Group gap="0.75rem">
            <DensityToggle value={density()} onChange={density.set} />
            <Text muted>Density: {density()}</Text>
        </Group>
    );
}

Slots: root option

PropTypeRequiredDefaultDescription
onChange(value: Density) => voidyesCalled with the density the user picks.
valueDensityyesCurrent density.
labelstring"Density"Accessible name (default "Density").

Density

type Density = "comfortable" | "compact";

ThemeToggle #

Toggle between light and dark themes.

This panel is in the light theme.

function Example() {
    const theme = signal<"light" | "dark">("light");
    return (
        <Paper withBorder class={theme() === "dark" ? "a-theme-dark" : undefined}>
            <Group gap="0.75rem">
                <ThemeToggle value={theme()} onChange={theme.set} />
                <Text>This panel is in the {theme()} theme.</Text>
            </Group>
        </Paper>
    );
}
PropTypeRequiredDefaultDescription
onChange(theme: "dark" | "light") => voidyesCalled with the theme to switch to; apply it yourself (e.g. data-theme on <html>).
value"dark" | "light"yesCurrent theme.

LocaleSwitcher #

Inline language picker.

function Example() {
    const locale = signal("en");
    return (
        <LocaleSwitcher
            label="Language"
            value={locale()}
            options={[
                { value: "en", label: "English" },
                { value: "de", label: "Deutsch" },
                { value: "ja", label: "日本語" },
            ]}
            onChange={locale.set}
        />
    );
}

Slots: root option

PropTypeRequiredDefaultDescription
onChange(value: string) => voidyesCalled with the picked locale code.
optionsLocaleOption[]yesAvailable languages.
valuestringyesCurrent locale code (controlled).
labelstring"Language"Accessible name (default "Language").

LocaleOption

FieldTypeRequiredDescription
valuestringyesLocale code, e.g. de.
labelstringyesLanguage name shown to the user.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.