Kobalte.v0.13.9

Segmented Control

A linear set of two or more segments, each of which functions as a radio input, and a default value is required.

Import

ts
import { SegmentedControl } from "@kobalte/core/segmented-control";
// or
import { Root, Label, ... } from "@kobalte/core/segmented-control";
ts
import { SegmentedControl } from "@kobalte/core/segmented-control";
// or
import { Root, Label, ... } from "@kobalte/core/segmented-control";

Features

  • Follow the WAI ARIA Radio Group design pattern.
  • Each segment is built with a native HTML <input> element, which is visually hidden to allow custom styling.
  • Syncs with form reset events.
  • Group and segment labeling support for assistive technology.
  • Can be controlled or uncontrolled.
  • Supports animatable indicator.

Anatomy

The segmented control consists of:

  • SegmentedControl: The root container for the segmented control.
  • SegmentedControl.Label: The label that gives the user information on the segmented control.
  • SegmentedControl.Description: The description that gives the user more information on the segmented control.
  • SegmentedControl.ErrorMessage: The error message that gives the user information about how to fix a validation error on the segmented control.
  • SegmentedControl.Indicator: The visual indicator displayed below the items to indicate the selected item.

The segmented control item consists of:

  • SegmentedControl.Item: The root container for an item's radio button.
  • SegmentedControl.ItemInput: The native html input that is visually hidden in the item's radio button.
  • SegmentedControl.ItemControl: The element that visually represents an item's radio button.
  • SegmentedControl.ItemIndicator: The visual indicator rendered when the item's radio button is in a checked state.
  • SegmentedControl.ItemLabel: The label that gives the user information on the item's radio button.
  • SegmentedControl.ItemDescription: The description that gives the user more information on the item's radio button.
tsx
<SegmentedControl>
<SegmentedControl.Label />
<SegmentedControl.Indicator />
<SegmentedControl.Item>
<SegmentedControl.ItemInput />
<SegmentedControl.ItemControl>
<SegmentedControl.ItemIndicator />
</SegmentedControl.ItemControl>
<SegmentedControl.ItemLabel />
<SegmentedControl.ItemDescription />
</SegmentedControl.Item>
<SegmentedControl.Description />
<SegmentedControl.ErrorMessage />
</SegmentedControl>
tsx
<SegmentedControl>
<SegmentedControl.Label />
<SegmentedControl.Indicator />
<SegmentedControl.Item>
<SegmentedControl.ItemInput />
<SegmentedControl.ItemControl>
<SegmentedControl.ItemIndicator />
</SegmentedControl.ItemControl>
<SegmentedControl.ItemLabel />
<SegmentedControl.ItemDescription />
</SegmentedControl.Item>
<SegmentedControl.Description />
<SegmentedControl.ErrorMessage />
</SegmentedControl>

Example

Favorite fruit
tsx
import { SegmentedControl } from "@kobalte/core/segmented-control";
import "./style.css";
function App() {
return (
<SegmentedControl class={style["segmented-control"]} defaultValue="Apple">
<SegmentedControl.Label class={style["segmented-control__label"]}>
Favorite fruit
</SegmentedControl.Label>
<div class={style["segmented-control__wrapper"]} role="presentation">
<SegmentedControl.Indicator class={style["segmented-control__indicator"]} />
<div class={style["segmented-control__items"]} role="presentation">
<For each={["Apple", "Orange", "Watermelon"]}>
{(fruit) => (
<SegmentedControl.Item value={fruit} class={style["segmented-control__item"]}>
<SegmentedControl.ItemInput class={style["segmented-control__item-input"]} />
<SegmentedControl.ItemLabel class={style["segmented-control__item-label"]}>
{fruit}
</SegmentedControl.ItemLabel>
</SegmentedControl.Item>
)}
</For>
</div>
</div>
</SegmentedControl>
);
}
tsx
import { SegmentedControl } from "@kobalte/core/segmented-control";
import "./style.css";
function App() {
return (
<SegmentedControl class={style["segmented-control"]} defaultValue="Apple">
<SegmentedControl.Label class={style["segmented-control__label"]}>
Favorite fruit
</SegmentedControl.Label>
<div class={style["segmented-control__wrapper"]} role="presentation">
<SegmentedControl.Indicator class={style["segmented-control__indicator"]} />
<div class={style["segmented-control__items"]} role="presentation">
<For each={["Apple", "Orange", "Watermelon"]}>
{(fruit) => (
<SegmentedControl.Item value={fruit} class={style["segmented-control__item"]}>
<SegmentedControl.ItemInput class={style["segmented-control__item-input"]} />
<SegmentedControl.ItemLabel class={style["segmented-control__item-label"]}>
{fruit}
</SegmentedControl.ItemLabel>
</SegmentedControl.Item>
)}
</For>
</div>
</div>
</SegmentedControl>
);
}

Usage

The role="presentation" is required for all non content elements between the SegmentedControl and SegmentedControl.Item due to a bug in Chromium based browsers that incorrectly parse semantics and break screen readers.

Controlled value

The value prop, which accepts a value corresponding with the value prop of each radio, can be used to make the value controlled. The onChange event is fired when the user selects a radio, and receives the new value.

Favorite fruit

Your favorite fruit is: Orange.

tsx
import { createSignal } from "solid-js";
function ControlledExample() {
const [value, setValue] = createSignal("Orange");
return (
<>
<SegmentedControl value={value()} onChange={setValue}>
<SegmentedControl.Label>Favorite fruit</SegmentedControl.Label>
<div role="presentation">
<SegmentedControl.Indicator />
<div role="presentation">
<For each={["Apple", "Orange", "Watermelon"]}>
{fruit => (
<SegmentedControl.Item value={fruit}>
<SegmentedControl.ItemInput />
<SegmentedControl.ItemLabel>{fruit}</SegmentedControl.ItemLabel>
</SegmentedControl.Item>
)}
</For>
</div>
</div>
</SegmentedControl>
<p class="not-prose text-sm mt-4">Your favorite fruit is: {value()}.</p>
</>
);
}
tsx
import { createSignal } from "solid-js";
function ControlledExample() {
const [value, setValue] = createSignal("Orange");
return (
<>
<SegmentedControl value={value()} onChange={setValue}>
<SegmentedControl.Label>Favorite fruit</SegmentedControl.Label>
<div role="presentation">
<SegmentedControl.Indicator />
<div role="presentation">
<For each={["Apple", "Orange", "Watermelon"]}>
{fruit => (
<SegmentedControl.Item value={fruit}>
<SegmentedControl.ItemInput />
<SegmentedControl.ItemLabel>{fruit}</SegmentedControl.ItemLabel>
</SegmentedControl.Item>
)}
</For>
</div>
</div>
</SegmentedControl>
<p class="not-prose text-sm mt-4">Your favorite fruit is: {value()}.</p>
</>
);
}

Description

The SegmentedControl.Description component can be used to associate additional help text with a segmented control.

Favorite fruit
Choose the fruit you like the most.
tsx
<SegmentedControl defaultValue="Apple">
<SegmentedControl.Label>Favorite fruit</SegmentedControl.Label>
<div role="presentation">
<SegmentedControl.Indicator />
<div role="presentation">
<For each={["Apple", "Orange", "Watermelon"]}>
{fruit => <SegmentedControl.Item value={fruit}>...</SegmentedControl.Item>}
</For>
</div>
</div>
<SegmentedControl.Description>Choose the fruit you like the most.</SegmentedControl.Description>
</SegmentedControl>
tsx
<SegmentedControl defaultValue="Apple">
<SegmentedControl.Label>Favorite fruit</SegmentedControl.Label>
<div role="presentation">
<SegmentedControl.Indicator />
<div role="presentation">
<For each={["Apple", "Orange", "Watermelon"]}>
{fruit => <SegmentedControl.Item value={fruit}>...</SegmentedControl.Item>}
</For>
</div>
</div>
<SegmentedControl.Description>Choose the fruit you like the most.</SegmentedControl.Description>
</SegmentedControl>

Error message

The SegmentedControl.ErrorMessage component can be used to help the user fix a validation error. It should be combined with the validationState prop to semantically mark the segmented control as invalid for assistive technologies.

By default, it will render only when the validationState prop is set to invalid, use the forceMount prop to always render the error message (ex: for usage with animation libraries).

Favorite fruit
Hmm, I prefer apples.
tsx
import { createSignal } from "solid-js";
function ErrorMessageExample() {
const [value, setValue] = createSignal("Orange");
return (
<SegmentedControl
value={value()}
onChange={setValue}
validationState={value() !== "Apple" ? "invalid" : "valid"}
>
<SegmentedControl.Label>Favorite fruit</SegmentedControl.Label>
<div role="presentation">
<SegmentedControl.Indicator />
<div role="presentation">
<For each={["Apple", "Orange", "Watermelon"]}>
{fruit => <SegmentedControl.Item value={fruit}>...</SegmentedControl.Item>}
</For>
</div>
</div>
<SegmentedControl.ErrorMessage>Hmm, I prefer apples.</SegmentedControl.ErrorMessage>
</SegmentedControl>
);
}
tsx
import { createSignal } from "solid-js";
function ErrorMessageExample() {
const [value, setValue] = createSignal("Orange");
return (
<SegmentedControl
value={value()}
onChange={setValue}
validationState={value() !== "Apple" ? "invalid" : "valid"}
>
<SegmentedControl.Label>Favorite fruit</SegmentedControl.Label>
<div role="presentation">
<SegmentedControl.Indicator />
<div role="presentation">
<For each={["Apple", "Orange", "Watermelon"]}>
{fruit => <SegmentedControl.Item value={fruit}>...</SegmentedControl.Item>}
</For>
</div>
</div>
<SegmentedControl.ErrorMessage>Hmm, I prefer apples.</SegmentedControl.ErrorMessage>
</SegmentedControl>
);
}

HTML forms

The segmented control name prop, paired with the item's radio value prop, can be used for integration with HTML forms.

Favorite fruit
tsx
function HTMLFormExample() {
const onSubmit = (e: SubmitEvent) => {
// handle form submission.
};
return (
<form onSubmit={onSubmit}>
<SegmentedControl name="favorite-fruit" defaultValue="Orange">
<SegmentedControl.Label>Favorite fruit</SegmentedControl.Label>
<div role="presentation">
<SegmentedControl.Indicator />
<div role="presentation">
<For each={["Apple", "Orange", "Watermelon"]}>
{fruit => <SegmentedControl.Item value={fruit}>...</SegmentedControl.Item>}
</For>
</div>
</div>
</SegmentedControl>
<div>
<button type="reset">Reset</button>
<button type="submit">Submit</button>
</div>
</form>
);
}
tsx
function HTMLFormExample() {
const onSubmit = (e: SubmitEvent) => {
// handle form submission.
};
return (
<form onSubmit={onSubmit}>
<SegmentedControl name="favorite-fruit" defaultValue="Orange">
<SegmentedControl.Label>Favorite fruit</SegmentedControl.Label>
<div role="presentation">
<SegmentedControl.Indicator />
<div role="presentation">
<For each={["Apple", "Orange", "Watermelon"]}>
{fruit => <SegmentedControl.Item value={fruit}>...</SegmentedControl.Item>}
</For>
</div>
</div>
</SegmentedControl>
<div>
<button type="reset">Reset</button>
<button type="submit">Submit</button>
</div>
</form>
);
}

Orientation

The segmented control orientation prop can be used to change the segmented control's orientation.

Favorite fruit
tsx
<SegmentedControl orientation="vertical" defaultValue="Apple">
<SegmentedControl.Label>Favorite fruit</SegmentedControl.Label>
<div role="presentation">
<SegmentedControl.Indicator />
<div role="presentation">
<For each={["Apple", "Orange", "Watermelon"]}>
{fruit => <SegmentedControl.Item value={fruit}>...</SegmentedControl.Item>}
</For>
</div>
</div>
<SegmentedControl.Description>Choose the fruit you like the most.</SegmentedControl.Description>
</SegmentedControl>
tsx
<SegmentedControl orientation="vertical" defaultValue="Apple">
<SegmentedControl.Label>Favorite fruit</SegmentedControl.Label>
<div role="presentation">
<SegmentedControl.Indicator />
<div role="presentation">
<For each={["Apple", "Orange", "Watermelon"]}>
{fruit => <SegmentedControl.Item value={fruit}>...</SegmentedControl.Item>}
</For>
</div>
</div>
<SegmentedControl.Description>Choose the fruit you like the most.</SegmentedControl.Description>
</SegmentedControl>

API Reference

SegmentedControl

SegmentedControl is equivalent to the Root import from @kobalte/core/segmented-control.

PropDescription
valuestring
The controlled value of the item's radio button to check.
defaultValuestring
The value of the item's radio button that should be checked when initially rendered. Useful when you do not need to control the state of the radio buttons.
onChange(value: string) => void
Event handler called when the value changes.
orientation'horizontal' | 'vertical'
The axis the segmented control items should align with.
namestring
The name of the segmented control. Submitted with its owning form as part of a name/value pair.
validationState'valid' | 'invalid'
Whether the segmented control should display its "valid" or "invalid" visual styling.
requiredboolean
Whether the user must check a segmented control item before the owning form can be submitted.
disabledboolean
Whether the segmented control is disabled.
readOnlyboolean
Whether the segmented control items can be selected but not changed by the user.
Data attributeDescription
data-validPresent when the segmented control is valid according to the validation rules.
data-invalidPresent when the segmented control is invalid according to the validation rules.
data-requiredPresent when the user must check a segmented control item before the owning form can be submitted.
data-disabledPresent when the segmented control is disabled.
data-readonlyPresent when the segmented control is read only.

SegmentedControl.Label, SegmentedControl.Description and SegmentedControl.ErrorMesssage shares the same data-attributes.

SegmentedControl.ErrorMessage

PropDescription
forceMountboolean
Used to force mounting when more control is needed. Useful when controlling animation with SolidJS animation libraries.

SegmentedControl.Item

PropDescription
valuestring
The value of the item's radio button, used when submitting an HTML form. See MDN.
disabledboolean
Whether the item's radio button is disabled or not.
Data attributeDescription
data-validPresent when the parent segmented control is valid according to the validation rules.
data-invalidPresent when the parent segmented control is invalid according to the validation rules.
data-checkedPresent when the segmented control checked.
data-disabledPresent when the segmented control disabled.

SegmentedControl.ItemInput, SegmentedControl.ItemControl, SegmentedControl.ItemIndicator and SegmentedControl.ItemLabel shares the same data-attributes.

SegmentedControl.ItemIndicator

PropDescription
forceMountboolean
Used to force mounting when more control is needed. Useful when controlling animation with SolidJS animation libraries.

Rendered elements

ComponentDefault rendered element
SegmentedControldiv
SegmentedControl.Labelspan
SegmentedControl.Descriptiondiv
SegmentedControl.ErrorMessagediv
SegmentedControl.Indicatordiv
SegmentedControl.Itemdiv
SegmentedControl.ItemInputinput
SegmentedControl.ItemControldiv
SegmentedControl.ItemIndicatordiv
SegmentedControl.ItemLabellabel
SegmentedControl.ItemDescriptiondiv

Accessibility

Keyboard Interactions

KeyDescription
TabMoves focus to either the checked item's radio button or the first item's radio button in the group.
SpaceWhen focus is on an unchecked item's radio button, checks it.
ArrowDownMoves focus and checks the next item's radio button in the group.
ArrowRightMoves focus and checks the next item's radio button in the group.
ArrowUpMoves focus and checks the previous item's radio button in the group.
ArrowLeftMoves focus and checks the previous item's radio button in the group.
Previous
Search
Next
Select