packages/codemod/docs/RADIO_GROUP_MIGRATION.md
This document outlines the migration from Chakra UI v2 RadioGroup component to v3.
import { Radio, RadioGroup } from "@chakra-ui/react"
;<RadioGroup onChange={setValue} value={value}>
<Stack direction="row">
<Radio value="1">First</Radio>
<Radio value="2">Second</Radio>
<Radio value="3">Third</Radio>
</Stack>
</RadioGroup>
import { RadioGroup } from "@chakra-ui/react"
;<RadioGroup.Root onValueChange={(e) => setValue(e.value)} value={value}>
<Stack direction="row">
<RadioGroup.Item value="1">
<RadioGroup.ItemHiddenInput />
<RadioGroup.ItemIndicator />
<RadioGroup.ItemText>First</RadioGroup.ItemText>
</RadioGroup.Item>
<RadioGroup.Item value="2">
<RadioGroup.ItemHiddenInput />
<RadioGroup.ItemIndicator />
<RadioGroup.ItemText>Second</RadioGroup.ItemText>
</RadioGroup.Item>
<RadioGroup.Item value="3">
<RadioGroup.ItemHiddenInput />
<RadioGroup.ItemIndicator />
<RadioGroup.ItemText>Third</RadioGroup.ItemText>
</RadioGroup.Item>
</Stack>
</RadioGroup.Root>
RadioGroup Component:
<RadioGroup> → <RadioGroup.Root>Radio Component:
<Radio> → <RadioGroup.Item> with required sub-components:
<RadioGroup.ItemHiddenInput /> - Hidden native input element<RadioGroup.ItemIndicator /> - Visual indicator (radio dot)<RadioGroup.ItemText> - Label text (wraps children)Nested Structure:
| v2 Prop | v3 Prop | Notes |
|---|---|---|
onChange | onValueChange | Receives object: { value } instead of just value |
value | value | No change |
defaultValue | defaultValue | No change |
size | size | No change |
variant | variant | No change |
colorScheme | colorPalette | Renamed (though colorPalette already works) |
| v2 Prop | v3 Prop | Notes |
|---|---|---|
isDisabled | disabled | Standard HTML attribute |
value | value | No change |
| v2 Prop | v3 Equivalent | Notes |
|---|---|---|
isInvalid | (removed) | Controlled from RadioGroup.Root |
isChecked | (removed) | Controlled from RadioGroup.Root |
defaultChecked | (removed) | Use RadioGroup.Root defaultValue |
colorScheme | (removed) | Should be set on RadioGroup.Root as colorPalette |
| v2 Prop | v3 Location | Notes |
|---|---|---|
inputProps | RadioGroup.ItemHiddenInput | Native input attributes moved to HiddenInput |
v2:
import { Radio, RadioGroup, Stack } from "@chakra-ui/react"
function App() {
return (
<RadioGroup defaultValue="2">
<Stack spacing={5} direction="row">
<Radio value="1">Radio 1</Radio>
<Radio value="2">Radio 2</Radio>
<Radio value="3">Radio 3</Radio>
</Stack>
</RadioGroup>
)
}
v3:
import { RadioGroup, Stack } from "@chakra-ui/react"
function App() {
return (
<RadioGroup.Root defaultValue="2">
<Stack spacing={5} direction="row">
<RadioGroup.Item value="1">
<RadioGroup.ItemHiddenInput />
<RadioGroup.ItemIndicator />
<RadioGroup.ItemText>Radio 1</RadioGroup.ItemText>
</RadioGroup.Item>
<RadioGroup.Item value="2">
<RadioGroup.ItemHiddenInput />
<RadioGroup.ItemIndicator />
<RadioGroup.ItemText>Radio 2</RadioGroup.ItemText>
</RadioGroup.Item>
<RadioGroup.Item value="3">
<RadioGroup.ItemHiddenInput />
<RadioGroup.ItemIndicator />
<RadioGroup.ItemText>Radio 3</RadioGroup.ItemText>
</RadioGroup.Item>
</Stack>
</RadioGroup.Root>
)
}
v2:
import { Radio, RadioGroup, Stack } from "@chakra-ui/react"
import React from "react"
function RadioExample() {
const [value, setValue] = React.useState("1")
return (
<RadioGroup onChange={setValue} value={value}>
<Stack direction="row">
<Radio value="1">First</Radio>
<Radio value="2">Second</Radio>
<Radio value="3">Third</Radio>
</Stack>
</RadioGroup>
)
}
v3:
import { RadioGroup, Stack } from "@chakra-ui/react"
import React from "react"
function RadioExample() {
const [value, setValue] = React.useState("1")
return (
<RadioGroup.Root onValueChange={(e) => setValue(e.value)} value={value}>
<Stack direction="row">
<RadioGroup.Item value="1">
<RadioGroup.ItemHiddenInput />
<RadioGroup.ItemIndicator />
<RadioGroup.ItemText>First</RadioGroup.ItemText>
</RadioGroup.Item>
<RadioGroup.Item value="2">
<RadioGroup.ItemHiddenInput />
<RadioGroup.ItemIndicator />
<RadioGroup.ItemText>Second</RadioGroup.ItemText>
</RadioGroup.Item>
<RadioGroup.Item value="3">
<RadioGroup.ItemHiddenInput />
<RadioGroup.ItemIndicator />
<RadioGroup.ItemText>Third</RadioGroup.ItemText>
</RadioGroup.Item>
</Stack>
</RadioGroup.Root>
)
}
v2:
import { Radio, RadioGroup, Stack } from "@chakra-ui/react"
function App() {
return (
<RadioGroup defaultValue="2">
<Stack spacing={5} direction="row">
<Radio colorScheme="red" value="1">
Radio 1
</Radio>
<Radio colorScheme="green" value="2">
Radio 2
</Radio>
</Stack>
</RadioGroup>
)
}
v3:
import { RadioGroup, Stack } from "@chakra-ui/react"
function App() {
return (
<RadioGroup.Root defaultValue="2" colorPalette="red">
<Stack spacing={5} direction="row">
<RadioGroup.Item value="1">
<RadioGroup.ItemHiddenInput />
<RadioGroup.ItemIndicator />
<RadioGroup.ItemText>Radio 1</RadioGroup.ItemText>
</RadioGroup.Item>
<RadioGroup.Item value="2">
<RadioGroup.ItemHiddenInput />
<RadioGroup.ItemIndicator />
<RadioGroup.ItemText>Radio 2</RadioGroup.ItemText>
</RadioGroup.Item>
</Stack>
</RadioGroup.Root>
)
}
Note: In v3, colorPalette is set on the Root, not on individual items. The
codemod automatically removes colorScheme from individual Radio elements.
v2:
import { Radio, RadioGroup, Stack } from "@chakra-ui/react"
function App() {
return (
<RadioGroup defaultValue="1">
<Stack>
<Radio value="1" isDisabled>
Checked
</Radio>
<Radio value="2">Unchecked</Radio>
<Radio value="3">Unchecked</Radio>
</Stack>
</RadioGroup>
)
}
v3:
import { RadioGroup, Stack } from "@chakra-ui/react"
function App() {
return (
<RadioGroup.Root defaultValue="1">
<Stack>
<RadioGroup.Item value="1" disabled>
<RadioGroup.ItemHiddenInput />
<RadioGroup.ItemIndicator />
<RadioGroup.ItemText>Checked</RadioGroup.ItemText>
</RadioGroup.Item>
<RadioGroup.Item value="2">
<RadioGroup.ItemHiddenInput />
<RadioGroup.ItemIndicator />
<RadioGroup.ItemText>Unchecked</RadioGroup.ItemText>
</RadioGroup.Item>
<RadioGroup.Item value="3">
<RadioGroup.ItemHiddenInput />
<RadioGroup.ItemIndicator />
<RadioGroup.ItemText>Unchecked</RadioGroup.ItemText>
</RadioGroup.Item>
</Stack>
</RadioGroup.Root>
)
}
v2:
import { Radio, RadioGroup } from "@chakra-ui/react"
function App() {
return (
<RadioGroup>
<Radio value="1" inputProps={{ "aria-label": "Option 1" }}>
Radio 1
</Radio>
<Radio value="2">Radio 2</Radio>
</RadioGroup>
)
}
v3:
import { RadioGroup } from "@chakra-ui/react"
function App() {
return (
<RadioGroup.Root>
<RadioGroup.Item value="1">
<RadioGroup.ItemHiddenInput inputProps={{ "aria-label": "Option 1" }} />
<RadioGroup.ItemIndicator />
<RadioGroup.ItemText>Radio 1</RadioGroup.ItemText>
</RadioGroup.Item>
<RadioGroup.Item value="2">
<RadioGroup.ItemHiddenInput />
<RadioGroup.ItemIndicator />
<RadioGroup.ItemText>Radio 2</RadioGroup.ItemText>
</RadioGroup.Item>
</RadioGroup.Root>
)
}
v2:
import { Radio, RadioGroup } from "@chakra-ui/react"
function App() {
return (
<RadioGroup size="lg" variant="outline" defaultValue="1">
<Radio value="1">Large Radio 1</Radio>
<Radio value="2">Large Radio 2</Radio>
</RadioGroup>
)
}
v3:
import { RadioGroup } from "@chakra-ui/react"
function App() {
return (
<RadioGroup.Root size="lg" variant="outline" defaultValue="1">
<RadioGroup.Item value="1">
<RadioGroup.ItemHiddenInput />
<RadioGroup.ItemIndicator />
<RadioGroup.ItemText>Large Radio 1</RadioGroup.ItemText>
</RadioGroup.Item>
<RadioGroup.Item value="2">
<RadioGroup.ItemHiddenInput />
<RadioGroup.ItemIndicator />
<RadioGroup.ItemText>Large Radio 2</RadioGroup.ItemText>
</RadioGroup.Item>
</RadioGroup.Root>
)
}
To automatically migrate your RadioGroup components, run:
npx @chakra-ui/codemod transform radio-group <path>
--dry - Do a dry-run without making changes--print - Print the changed output for comparison# Transform all files in src directory
npx @chakra-ui/codemod transform radio-group ./src
# Dry run to preview changes
npx @chakra-ui/codemod transform radio-group ./src --dry
# Print changes for comparison
npx @chakra-ui/codemod transform radio-group ./src --print
If you prefer to migrate manually:
Update Component Names:
<RadioGroup> → <RadioGroup.Root><Radio> → <RadioGroup.Item>Add Required Sub-components for each Radio:
<RadioGroup.ItemHiddenInput /> as first child<RadioGroup.ItemIndicator /> as second child<RadioGroup.ItemText>Update RadioGroup Props:
onChange → onValueChange (receives { value } object)Update Radio Props:
isDisabled → disabledisInvalid, isChecked, defaultChecked (controlled from Root)inputProps to ItemHiddenInputcolorScheme from individual items (set on Root as colorPalette)RadioGroup → RadioGroup.Root (namespace component)Radio → RadioGroup.Item (requires sub-components)onChange → onValueChange on RadioGroup.RootisDisabled → disabled on RadioGroup.ItemisInvalid - controlled from RadioGroup.RootisChecked - controlled from RadioGroup.RootdefaultChecked - use RadioGroup.Root defaultValuecolorScheme on individual items - set on RadioGroup.Root as colorPaletteonChange(value) → onValueChange({ value })value property instead of just the valueinputProps moved from Radio to RadioGroup.ItemHiddenInputThe codemod will:
<RadioGroup> to <RadioGroup.Root> with proper structure<Radio> to <RadioGroup.Item> with all required sub-components<RadioGroup.ItemHiddenInput /> automatically<RadioGroup.ItemIndicator /> automatically<RadioGroup.ItemText>onChange to onValueChange on RadioGroup.RootisDisabled to disabled on RadioGroup.ItemisInvalid, isChecked, defaultChecked propscolorScheme from individual itemsinputProps to ItemHiddenInputThe v3 RadioGroup component provides:
onValueChange handler receives an object { value }
instead of just the value:
onChange={(value) => setValue(value)}onValueChange={(e) => setValue(e.value)}<RadioGroup.Item> requires three sub-components for proper
functionality<RadioGroup.ItemHiddenInput /> is required for proper form integration and
accessibilitycolorScheme/colorPalette should be set on RadioGroup.Root, not on
individual items