RadioGroup
Use in a form to allow selecting one option from multiple
`
`
`
- Accessible, easy to compose and customize.
- Sizable & works controlled or uncontrolled.
- Ability to opt-out to native radio button on web.
Installation
RadioGroup is already installed in @hanzo/gui, or you can install it independently:
npm install @hanzogui/radio-groupRadioGroup is already installed in @hanzo/gui, or you can install it independently:
npm install @hanzogui/radio-groupTo use the headless radio group, import from the @hanzogui/radio-headless package. This package has no dependency on @hanzogui/core and provides hooks for building custom radio groups with any styling solution.
npm install @hanzogui/radio-headlessUsage
import { RadioGroup } from '@hanzo/gui'
export default () => (
<RadioGroup value="foo" gap="$2">
<RadioGroup.Item value="foo" id="foo-radio-item">
<RadioGroup.Indicator />
</RadioGroup.Item>
<RadioGroup.Item value="bar" id="bar-radio-item">
<RadioGroup.Indicator />
</RadioGroup.Item>
</RadioGroup>
)Use the createRadioGroup export to create a fully custom radio group that still uses the Hanzo GUI styling system. You provide your own styled components and get back a fully functional radio group component.
import { createRadioGroup, RadioGroupContext } from '@hanzogui/radio-group'
import { styled, View } from '@hanzo/gui'
const CustomFrame = styled(View, {
// your styles
})
const CustomItem = styled(View, {
context: RadioGroupContext,
// your styles
})
const CustomIndicator = styled(View, {
context: RadioGroupContext,
// your styles
})
export const CustomRadioGroup = createRadioGroup({
Frame: CustomFrame,
Item: CustomItem,
Indicator: CustomIndicator,
})The @hanzogui/radio-headless package provides three hooks for building custom radio groups:
useRadioGroup- for the container/groupuseRadioGroupItem- for individual radio itemsuseRadioGroupItemIndicator- for the indicator (checked state visual)
Basic Usage
import {
useRadioGroup,
useRadioGroupItem,
useRadioGroupItemIndicator,
RadioGroupContextValue,
RadioGroupItemContextValue,
} from '@hanzogui/radio-headless'
import { createContext, useContext } from 'react'
// Create contexts for the group and item
const RadioGroupContext = createContext<RadioGroupContextValue>({})
const RadioGroupItemContext = createContext<RadioGroupItemContextValue>({
checked: false,
})
function RadioGroup({ children, ...props }) {
const { providerValue, frameAttrs } = useRadioGroup({
orientation: 'vertical',
...props,
})
return (
<RadioGroupContext.Provider value={providerValue}>
<div {...frameAttrs} style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
{children}
</div>
</RadioGroupContext.Provider>
)
}
function RadioItem({ value, children }) {
const { checked, providerValue, frameAttrs, bubbleInput } = useRadioGroupItem({
radioGroupContext: RadioGroupContext,
value,
})
return (
<RadioGroupItemContext.Provider value={providerValue}>
<button
{...frameAttrs}
style={{
display: 'flex',
alignItems: 'center',
gap: 8,
padding: 8,
border: '1px solid #ccc',
borderRadius: 4,
background: checked ? '#e0f2fe' : 'white',
}}
>
<RadioIndicator />
{children}
</button>
{bubbleInput}
</RadioGroupItemContext.Provider>
)
}
function RadioIndicator() {
const { checked, ...indicatorProps } = useRadioGroupItemIndicator({
radioGroupItemContext: RadioGroupItemContext,
})
return (
<div
{...indicatorProps}
style={{
width: 16,
height: 16,
borderRadius: '50%',
border: '2px solid #3b82f6',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{checked && (
<div
style={{ width: 8, height: 8, borderRadius: '50%', background: '#3b82f6' }}
/>
)}
</div>
)
}
// Usage
function App() {
return (
<RadioGroup defaultValue="option1" onValueChange={console.log}>
<RadioItem value="option1">Option 1</RadioItem>
<RadioItem value="option2">Option 2</RadioItem>
<RadioItem value="option3">Option 3</RadioItem>
</RadioGroup>
)
}API Reference
RadioGroup
RadioGroup extends Stack views inheriting all the Gui standard props, plus:
When using createRadioGroup, you provide styled components:
Frame: The root container componentItem: The radio item componentIndicator: The indicator component
The useRadioGroup hook accepts these options:
| Prop | Type | Default | Required |
|---|---|---|---|
| name | string | - | - |
| value | string | - | - |
| defaultValue | string | - | - |
| required | boolean | - | - |
| disabled | boolean | - | - |
| native | boolean | false | - |
| onValueChange | (value: string) => void | - | - |
| orientation | "horizontal" | "vertical" | - | - |
| accentColor | string | - | - |
RadioGroup.Item
| Prop | Type | Default | Required |
|---|---|---|---|
| labeledBy | string | - | - |
| value | string | - | - |
| disabled | boolean | - | - |
| id | string | - | - |
| scaleSize | number | 0.5 | - |
| unstyled | boolean | false | - |
RadioGroup.Indicator
RadioGroup.Indicator appears only when the parent Item is checked. It extends ThemeableStack, getting Gui standard props adding:
| Prop | Type | Default | Required |
|---|---|---|---|
| unstyled | boolean | - | - |
useRadioGroup Return Value
| Property | Type | Description |
|---|---|---|
providerValue | RadioGroupContextValue | Value to pass to your context provider |
frameAttrs | object | Props to spread on the group container (role, aria-orientation, etc.) |
rovingFocusGroupAttrs | object | Props for roving focus behavior (optional) |
useRadioGroupItem
Hook for individual radio items. Requires a context containing the group state.
const {
checked,
isFormControl,
providerValue,
bubbleInput,
native,
frameAttrs,
rovingFocusGroupAttrs,
} = useRadioGroupItem({
radioGroupContext: YourRadioGroupContext,
value: 'option1',
id: 'option1-id',
labelledBy: 'label-id',
disabled: false,
onPress: () => {},
onKeyDown: () => {},
onFocus: () => {},
})| Property | Type | Description |
|---|---|---|
checked | boolean | Whether this item is currently selected |
providerValue | object | Context value for the indicator |
frameAttrs | object | Props to spread on the item element |
bubbleInput | ReactNode | Hidden input for form compatibility |
native | boolean | Whether using native radio |
isFormControl | boolean | Whether inside a form |
useRadioGroupItemIndicator
Hook for the visual indicator of checked state.
const { checked, ...dataAttrs } = useRadioGroupItemIndicator({
radioGroupItemContext: YourRadioGroupItemContext,
disabled: false,
})| Property | Type | Description |
|---|---|---|
checked | boolean | Whether the parent item is checked |
data-state | string | 'checked' or 'unchecked' |
data-disabled | string | undefined | Present when disabled |
Last updated on