{"slug":"radio-group","title":"Radio Group","description":"Using the radio group machine in your project.","contentType":"component","framework":"react","content":"A radio group lets users select one option from a set.\n\n## Resources\n\n\n[Latest version: v1.39.1](https://www.npmjs.com/package/@zag-js/radio-group)\n[Logic Visualizer](https://zag-visualizer.vercel.app/radio-group)\n[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/radio-group)\n\n\n\n**Features**\n\n- Syncs with `disabled` state of fieldset\n- Syncs with form `reset` events\n- Can programmatically set radio group value\n- Can programmatically focus and blur radio items\n\n## Installation\n\nInstall the radio package:\n\n```bash\nnpm install @zag-js/radio-group @zag-js/react\n# or\nyarn add @zag-js/radio-group @zag-js/react\n```\n\n## Anatomy\n\nTo set up the radio group correctly, you'll need to understand its anatomy and\nhow we name its parts.\n\n> Each part includes a `data-part` attribute to help identify them in the DOM.\n\n\n\n## Usage\n\nImport the radio group package:\n\n```tsx\nimport * as radio from \"@zag-js/radio-group\"\n```\n\nThe radio package exports two key functions:\n\n- `machine` - State machine logic.\n- `connect` - Maps machine state to JSX props and event handlers.\n\n> Pass a unique `id` to `useMachine` so generated element ids stay predictable.\n\nThen use the framework integration helpers:\n\n```tsx\nimport * as radio from \"@zag-js/radio-group\"\nimport { useMachine, normalizeProps } from \"@zag-js/react\"\nimport { useId } from \"react\"\n\nconst items = [\n  { id: \"apple\", label: \"Apples\" },\n  { id: \"orange\", label: \"Oranges\" },\n  { id: \"mango\", label: \"Mangoes\" },\n  { id: \"grape\", label: \"Grapes\" },\n]\n\nfunction Radio() {\n  const service = useMachine(radio.machine, { id: useId() })\n\n  const api = radio.connect(service, normalizeProps)\n\n  return (\n    <div {...api.getRootProps()}>\n      <h3 {...api.getLabelProps()}>Fruits</h3>\n      {items.map((opt) => (\n        <label key={opt.id} {...api.getItemProps({ value: opt.id })}>\n          <span {...api.getItemTextProps({ value: opt.id })}>{opt.label}</span>\n          <input {...api.getItemHiddenInputProps({ value: opt.id })} />\n          <div {...api.getItemControlProps({ value: opt.id })} />\n        </label>\n      ))}\n    </div>\n  )\n}\n```\n\n### Disabling the radio group\n\nSet `disabled` to `true` to disable all radio items.\n\n```tsx {2}\nconst service = useMachine(radio.machine, {\n  disabled: true,\n})\n```\n\n### Setting the initial value\n\nUse the `defaultValue` property to set the radio group's initial value.\n\n```tsx {2}\nconst service = useMachine(radio.machine, {\n  defaultValue: \"apple\",\n})\n```\n\n### Controlled value\n\nUse `value` and `onValueChange` to control selection externally.\n\n```tsx\nconst service = useMachine(radio.machine, {\n  value,\n  onValueChange(details) {\n    setValue(details.value)\n  },\n})\n```\n\n### Listening for changes\n\nWhen the radio group value changes, the `onValueChange` callback is invoked.\n\n```tsx {2-7}\nconst service = useMachine(radio.machine, {\n  onValueChange(details) {\n    // details => { value: string | null }\n    console.log(\"radio value is:\", details.value)\n  },\n})\n```\n\n### Usage within forms\n\nTo use radio group in forms, set `name`.\n\n```tsx {2}\nconst service = useMachine(radio.machine, {\n  name: \"fruits\",\n})\n```\n\nSet `form` if the radio inputs should submit with a form outside the current DOM\nsubtree.\n\n```tsx\nconst service = useMachine(radio.machine, {\n  name: \"fruits\",\n  form: \"checkout-form\",\n})\n```\n\n### Vertical orientation\n\nSet `orientation` when you need a vertical layout.\n\n```tsx\nconst service = useMachine(radio.machine, {\n  orientation: \"vertical\",\n})\n```\n\n### Read-only and required state\n\nUse `readOnly` and `required` to control form behavior.\n\n```tsx\nconst service = useMachine(radio.machine, {\n  readOnly: true,\n  required: true,\n})\n```\n\n### Invalid state\n\nSet `invalid` to style and expose invalid form state.\n\n```tsx\nconst service = useMachine(radio.machine, {\n  invalid: true,\n})\n```\n\n## Styling guide\n\nEach radio part includes a `data-part` attribute you can target in CSS.\n\n### Checked State\n\nWhen the radio input is checked, the `data-state` attribute is added to the\nitem, control, and label parts.\n\n```css\n[data-part=\"radio\"][data-state=\"checked|unchecked\"] {\n  /* styles for radio checked or unchecked state */\n}\n\n[data-part=\"radio-control\"][data-state=\"checked|unchecked\"] {\n  /* styles for radio checked or unchecked state */\n}\n\n[data-part=\"radio-label\"][data-state=\"checked|unchecked\"] {\n  /* styles for radio checked or unchecked state */\n}\n```\n\n### Focused State\n\nWhen the radio input is focused, the `data-focus` attribute is added to the\nroot, control and label parts.\n\n```css\n[data-part=\"radio\"][data-focus] {\n  /* styles for radio focus state */\n}\n\n[data-part=\"radio-control\"][data-focus] {\n  /* styles for radio control focus state */\n}\n\n[data-part=\"radio-label\"][data-focus] {\n  /* styles for radio label focus state */\n}\n```\n\n### Disabled State\n\nWhen the radio is disabled, the `data-disabled` attribute is added to the root,\ncontrol and label parts.\n\n```css\n[data-part=\"radio\"][data-disabled] {\n  /* styles for radio disabled state */\n}\n\n[data-part=\"radio-control\"][data-disabled] {\n  /* styles for radio control disabled state */\n}\n\n[data-part=\"radio-label\"][data-disabled] {\n  /* styles for radio label disabled state */\n}\n```\n\n### Invalid State\n\nWhen the radio is invalid, the `data-invalid` attribute is added to the root,\ncontrol and label parts.\n\n```css\n[data-part=\"radio\"][data-invalid] {\n  /* styles for radio invalid state */\n}\n\n[data-part=\"radio-control\"][data-invalid] {\n  /* styles for radio control invalid state */\n}\n\n[data-part=\"radio-label\"][data-invalid] {\n  /* styles for radio label invalid state */\n}\n```\n\n## Methods and Properties\n\n### Machine Context\n\nThe radio group machine exposes the following context properties:\n\n**`ids`**\nType: `Partial<{ root: string; label: string; indicator: string; item: (value: string) => string; itemLabel: (value: string) => string; itemControl: (value: string) => string; itemHiddenInput: (value: string) => string; }>`\nDescription: The ids of the elements in the radio. Useful for composition.\n\n**`value`**\nType: `string`\nDescription: The controlled value of the radio group\n\n**`defaultValue`**\nType: `string`\nDescription: The initial value of the checked radio when rendered.\nUse when you don't need to control the value of the radio group.\n\n**`name`**\nType: `string`\nDescription: The name of the input fields in the radio\n(Useful for form submission).\n\n**`form`**\nType: `string`\nDescription: The associate form of the underlying input.\n\n**`disabled`**\nType: `boolean`\nDescription: If `true`, the radio group will be disabled\n\n**`invalid`**\nType: `boolean`\nDescription: If `true`, the radio group is marked as invalid.\n\n**`required`**\nType: `boolean`\nDescription: If `true`, the radio group is marked as required.\n\n**`readOnly`**\nType: `boolean`\nDescription: Whether the radio group is read-only\n\n**`onValueChange`**\nType: `(details: ValueChangeDetails) => void`\nDescription: Function called once a radio is checked\n\n**`orientation`**\nType: `\"horizontal\" | \"vertical\"`\nDescription: Orientation of the radio group\n\n**`dir`**\nType: `\"ltr\" | \"rtl\"`\nDescription: The document's text/writing direction.\n\n**`id`**\nType: `string`\nDescription: The unique identifier of the machine.\n\n**`getRootNode`**\nType: `() => ShadowRoot | Node | Document`\nDescription: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.\n\n### Machine API\n\nThe radio group `api` exposes the following methods:\n\n**`value`**\nType: `string`\nDescription: The current value of the radio group\n\n**`setValue`**\nType: `(value: string) => void`\nDescription: Function to set the value of the radio group\n\n**`clearValue`**\nType: `VoidFunction`\nDescription: Function to clear the value of the radio group\n\n**`focus`**\nType: `VoidFunction`\nDescription: Function to focus the radio group\n\n**`getItemState`**\nType: `(props: ItemProps) => ItemState`\nDescription: Returns the state details of a radio input\n\n## Accessibility\n\nAdheres to the\n[Radio Group WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/radiobutton)\n\n### Keyboard Interactions\n\n**`Tab`**\nDescription: Moves focus to either the checked radio item or the first radio item in the group.\n\n**`Space`**\nDescription: When focus is on an unchecked radio item, checks it.\n\n**`ArrowDown`**\nDescription: Moves focus and checks the next radio item in the group.\n\n**`ArrowRight`**\nDescription: Moves focus and checks the next radio item in the group.\n\n**`ArrowUp`**\nDescription: Moves focus to the previous radio item in the group.\n\n**`ArrowLeft`**\nDescription: Moves focus to the previous radio item in the group.","package":"@zag-js/radio-group","editUrl":"https://github.com/chakra-ui/zag/edit/main/website/data/components/radio-group.mdx"}