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

Radio Group

A radio group allows users to make a single choice from a select number of option

Fruits

Properties

Features

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

Installation

To use the radio machine in your project, run the following command in your command line:

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

Anatomy

To set up the radio group 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 🔥

<script setup> import * as radio from "@zag-js/radio-group" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed } from "vue" const items = [ { id: "apple", label: "Apples" }, { id: "orange", label: "Oranges" }, { id: "mango", label: "Mangoes" }, { id: "grape", label: "Grapes" }, ] const service = useMachine(radio.machine, { id: "1" }) const api = computed(() => radio.connect(service, normalizeProps)) </script> <template> <div v-bind="api.getRootProps()"> <h3 v-bind="api.getLabelProps()">Fruits</h3> <div v-for="opt in items" :key="opt.id"> <label v-bind="api.getItemProps({ value: opt.id })"> <span v-bind="api.getItemTextProps({ value: opt.id })" >{{ opt.label }}</span > <input v-bind="api.getItemHiddenInputProps({ value: opt.id })" /> <div v-bind="api.getItemControlProps({ value: opt.id })" /> </label> </div> </div> </template>

Disabling the radio group

To make a radio group disabled, set the context's disabled property to true

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

Setting the initial value

To set the radio group'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, { defaultValue: "apple", })

Listening for changes

When the radio group value changes, the onValueChange callback is invoked.

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

Usage within forms

To use radio group 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 radio part has a data-part attribute added to them to select and style them in the DOM.

Checked State

When the radio input is checked, the data-state attribute is added to the

[data-part="radio"][data-state="checked|unchecked"] { /* styles for radio checked or unchecked state */ } [data-part="radio-control"][data-state="checked|unchecked"] { /* styles for radio checked or unchecked state */ } [data-part="radio-label"][data-state="checked|unchecked"] { /* styles for radio checked or unchecked state */ }

Focused State

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

[data-part="radio"][data-focus] { /* styles for radio focus state */ } [data-part="radio-control"][data-focus] { /* styles for radio control 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, control and label parts.

[data-part="radio"][data-disabled] { /* styles for radio disabled state */ } [data-part="radio-control"][data-disabled] { /* styles for radio control 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, control and label parts.

[data-part="radio"][data-invalid] { /* styles for radio invalid state */ } [data-part="radio-control"][data-invalid] { /* styles for radio control 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

Accessibility

Adheres to the Radio Group WAI-ARIA design pattern

Keyboard Interactions

  • Tab
    Moves focus to either the checked radio item or the first radio item in the group.
  • Space
    When focus is on an unchecked radio item, checks it.
  • ArrowDown
    Moves focus and checks the next radio item in the group.
  • ArrowRight
    Moves focus and checks the next radio item in the group.
  • ArrowUp
    Moves focus to the previous radio item in the group.
  • ArrowLeft
    Moves focus to the previous radio item in the group.

Edit this page on GitHub

Proudly made in🇳🇬by Segun Adebayo

Copyright © 2025
On this page