Skip to main content
View Zag.js on Github
Join the Discord server

Segmented Control

A Segmented control allows users to make a single selection from multiple exclusive options, providing a visually distinct and intuitive way of interacting with radio inputs.

Properties

Features

  • Syncs with disabled state of fieldset
  • Syncs with form reset events
  • Can programmatically set segmented control value
  • Can programmatically focus and blur segmented control items

Installation

To use segmented control add the radio machine to your project, run the following command in your command line:

npm install @zag-js/radio-group @zag-js/react # or yarn add @zag-js/radio-group @zag-js/react

Anatomy

To set up the segmented control correctly, you'll need to understand its anatomy and how we name its parts.

Each part includes a data-part attribute to help identify them in the DOM.

Usage

First, import the radio group package into your project

import * as radio from "@zag-js/radio-group"

The radio package exports two key functions:

  • machine — The state machine logic for the radio widget.
  • connect — The function that translates the machine's state to JSX attributes and event handlers.

You'll also need to provide a unique id to the useMachine hook. This is used to ensure that every part has a unique identifier.

Next, import the required hooks and functions for your framework and use the radio machine in your project 🔥

import * as radio from "@zag-js/radio-group" import { useMachine, normalizeProps } from "@zag-js/react" const items = [ { label: "React", value: "react" }, { label: "Angular", value: "ng" }, { label: "Vue", value: "vue" }, { label: "Svelte", value: "svelte" }, ] function Radio() { const service = useMachine(radio.machine, { id: "1" }) const api = radio.connect(service, normalizeProps) return ( <div {...api.getRootProps()}> <div {...api.getIndicatorProps()} /> {items.map((opt) => ( <label key={opt.value} {...api.getItemProps({ value: opt.value })}> <span {...api.getItemTextProps({ value: opt.value })}> {opt.label} </span> <input {...api.getItemHiddenInputProps({ value: opt.value })} /> </label> ))} </div> ) }

Disabling the segmented control

To make a segmented control disabled, set the context's disabled property to true

const service = useMachine(radio.machine, { disabled: true, })

Making the segmented control readonly

To make a segmented control readonly, set the context's readOnly property to true

const service = useMachine(radio.machine, { readOnly: true, })

Setting the initial value

To set the segmented control's initial value, set the context's value property to the value of the radio item to be selected by default

const service = useMachine(radio.machine, { value: "apple", })

Listening for changes

When the segmented control value changes, the onValueChange callback is invoked.

const service = useMachine(radio.machine, { onValueChange(details) { // details => { value: string } console.log("segmented control value is:", details.value) }, })

Usage within forms

To use segmented control within forms, use the exposed inputProps from the connect function and ensure you pass name value to the machine's context. It will render a hidden input and ensure the value changes get propagated to the form correctly.

const service = useMachine(radio.machine, { name: "fruits", })

Styling guide

Earlier, we mentioned that each segmented control part has a data-part attribute added to them to select and style them in the DOM.

Indicator

Style the segmented control Indicator through the indicator part.

[data-part="indicator"] { /* styles for indicator */ }

Focused State

When the radio input is focused, the data-focus attribute is added to the root and label parts.

[data-part="radio"][data-focus] { /* styles for radio focus state */ } [data-part="radio-label"][data-focus] { /* styles for radio label focus state */ }

Disabled State

When the radio is disabled, the data-disabled attribute is added to the root and label parts.

[data-part="radio"][data-disabled] { /* styles for radio disabled state */ } [data-part="radio-label"][data-disabled] { /* styles for radio label disabled state */ }

Invalid State

When the radio is invalid, the data-invalid attribute is added to the root and label parts.

[data-part="radio"][data-invalid] { /* styles for radio invalid state */ } [data-part="radio-label"][data-invalid] { /* styles for radio label invalid state */ }

Methods and Properties

Machine Context

The radio group machine exposes the following context properties:

  • idsPartial<{ root: string; label: string; indicator: string; item(value: string): string; itemLabel(value: string): string; itemControl(value: string): string; itemHiddenInput(value: string): string; }>The ids of the elements in the radio. Useful for composition.
  • valuestringThe controlled value of the radio group
  • defaultValuestringThe initial value of the checked radio when rendered. Use when you don't need to control the value of the radio group.
  • namestringThe name of the input fields in the radio (Useful for form submission).
  • formstringThe associate form of the underlying input.
  • disabledbooleanIf `true`, the radio group will be disabled
  • readOnlybooleanWhether the checkbox is read-only
  • onValueChange(details: ValueChangeDetails) => voidFunction called once a radio is checked
  • orientation"horizontal" | "vertical"Orientation of the radio group
  • dir"ltr" | "rtl"The document's text/writing direction.
  • idstringThe unique identifier of the machine.
  • getRootNode() => ShadowRoot | Node | DocumentA root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.

Machine API

The radio group api exposes the following methods:

  • valuestringThe current value of the radio group
  • setValue(value: string) => voidFunction to set the value of the radio group
  • clearValue() => voidFunction to clear the value of the radio group
  • focus() => voidFunction to focus the radio group
  • getItemState(props: ItemProps) => ItemStateReturns the state details of a radio input

Data Attributes

Root
data-scope
radio-group
data-part
root
data-orientation
The orientation of the radio-group
data-disabled
Present when disabled
Label
data-scope
radio-group
data-part
label
data-orientation
The orientation of the label
data-disabled
Present when disabled
ItemControl
data-scope
radio-group
data-part
item-control
data-active
Present when active or pressed
Indicator
data-scope
radio-group
data-part
indicator
data-disabled
Present when disabled
data-orientation
The orientation of the indicator

Edit this page on GitHub

Proudly made in🇳🇬by Segun Adebayo

Copyright © 2025
On this page