Radio
Available from version 0.2.0
Radio buttons enable the user to select one option from a set.
Basics
import { Radio } from 'tailwind-joy/components';
The Tailwind-Joy Radio button behaves similar to the native HTML <input type="radio">
, so it accepts props like checked
, value
and onChange
.
import { Box, Radio } from 'tailwind-joy/components';
export function RadioBasics() {
return (
<Box className="flex flex-wrap justify-center gap-6">
<Radio name="radio-basics" label="One" value="one" defaultChecked />
<Radio name="radio-basics" label="Two" value="two" />
</Box>
);
}
Customization
Variants
The Radio component supports four variants: solid
, soft
, outlined
(default), and plain
.
import { Box, Radio } from 'tailwind-joy/components';
export function RadioVariants() {
return (
<Box className="flex flex-wrap justify-center gap-6">
<Radio name="radio-variants" label="Solid" variant="solid" />
<Radio name="radio-variants" label="Soft" variant="soft" />
<Radio
name="radio-variants"
label="Outlined"
variant="outlined"
defaultChecked
/>
<Radio name="radio-variants" label="Plain" variant="plain" />
</Box>
);
}
Sizes
The Radio component supports three sizes: sm
, md
(default), and lg
.
import { Box, Radio } from 'tailwind-joy/components';
export function RadioSizes() {
return (
<Box className="flex flex-wrap items-center justify-center gap-6">
<Radio name="radio-sizes" label="Small" size="sm" />
<Radio name="radio-sizes" label="Medium" size="md" defaultChecked />
<Radio name="radio-sizes" label="Large" size="lg" />
</Box>
);
}
Colors
The Radio component supports five colors: primary
, neutral
, danger
, success
, and warning
.
By default, when unchecked, the Radio is set to neutral
; when checked, the color changes to primary
.
import { Box, Radio } from 'tailwind-joy/components';
export function RadioColors() {
return (
<Box className="flex flex-wrap justify-center gap-6">
<Radio name="radio-colors" label="Primary" color="primary" />
<Radio name="radio-colors" label="Neutral" color="neutral" />
<Radio name="radio-colors" label="Danger" color="danger" />
<Radio name="radio-colors" label="Success" color="success" />
<Radio name="radio-colors" label="Warning" color="warning" />
</Box>
);
}
Position
To swap the positions of a Radio and its label, use the CSS property flex-direction: row-reverse
.
import { Box, Radio } from 'tailwind-joy/components';
export function RadioPosition() {
return (
<Box className="flex flex-wrap justify-center gap-6">
<Radio name="radio-position" label="One" className="flex-row-reverse" />
<Radio name="radio-position" label="Two" className="flex-row-reverse" />
</Box>
);
}
Focus outline
By default, the focus outline wraps both the Radio button and its label.
If you need to focus to omit the label, target the tj-radio-radio
class and add position: 'relative'
.
import { Box, Radio } from 'tailwind-joy/components';
export function RadioFocusOutline() {
return (
<div>
<div className="text-joy-neutral-600 dark:text-joy-neutral-400 mb-4 text-sm font-semibold">
Select an option and use keyboard ↑↓.
</div>
<Box className="flex flex-wrap justify-center gap-6">
<Radio name="radio-focus-outline" label="Fully wrapped" />
<Radio
name="radio-focus-outline"
label="Input wrapped"
className="[&>.tj-radio-radio]:relative"
/>
</Box>
</div>
);
}
Clickable container
To make the Radio button's container clickable, use the overlay
prop.
This works with any positioned wrapper element:
import { Radio, Sheet } from 'tailwind-joy/components';
export function RadioClickableContainer() {
return (
<div>
<div className="text-joy-neutral-600 dark:text-joy-neutral-400 mb-4 text-sm font-semibold">
Try using keyboard navigation.
</div>
<Sheet variant="outlined" className="flex rounded-[8px] p-4">
<Radio name="radio-clickable-container" label="Focus on me" overlay />
</Sheet>
</div>
);
}
Usage with Radio Group
Available from version 0.3.0
The Radio Group component is the ideal wrapper for multiple Radio components.
Controlled
Use the value
and onChange
props to control the actions performed by the Radio buttons.
For example, the Radio buttons in the demo below update the state to reflect the selected option:
import { useState } from 'react';
import { Radio, RadioGroup } from 'tailwind-joy/components';
type Variant = 'solid' | 'soft' | 'outlined' | 'plain';
export function RadioGroupControlled() {
const [variant, setVariant] = useState<Variant>('outlined');
return (
<RadioGroup
name="radio-group-controlled"
value={variant}
onChange={(e) => setVariant(e.currentTarget.value as Variant)}
>
<Radio value="solid" label="Solid" variant={variant} />
<Radio value="soft" label="Soft" variant={variant} />
<Radio value="outlined" label="Outlined" variant={variant} />
<Radio value="plain" label="Plain" variant={variant} />
</RadioGroup>
);
}
Focus outline
By default, the focus outline wraps both the Radio button and its label.
If you need to focus to omit the label, target the tj-radio-radio
class and add position: 'relative'
.
import { Radio, RadioGroup } from 'tailwind-joy/components';
export function RadioGroupFocusOutline() {
return (
<div>
<div className="text-joy-neutral-600 dark:text-joy-neutral-400 mb-4 text-sm font-semibold">
Select an option and use keyboard ↑↓.
</div>
<RadioGroup name="radio-group-focus-outline">
<Radio value="fully-wrapped" label="Fully wrapped" />
<Radio
value="input-wrapped"
label="Input wrapped"
className="[&>.tj-radio-radio]:relative"
/>
</RadioGroup>
</div>
);
}
Clickable container
To make the Radio button's container clickable, use the overlay
prop.
This works with any positioned wrapper element:
You can also apply this prop directly to a Radio Group when present, which will pass the prop to each individual Radio button nested within.
import { Radio, RadioGroup, Sheet } from 'tailwind-joy/components';
export function RadioGroupClickableContainer() {
return (
<RadioGroup
name="radio-group-clickable-container"
defaultValue="tailwind"
orientation="horizontal"
overlay
className="gap-4"
>
{[
{
label: 'Tailwind',
avatar: '/img/avatar/tailwind-gravatar.png',
},
{
label: 'Joy',
avatar: '/img/avatar/joy-gravatar.png',
},
{
label: 'Octocat',
avatar: '/img/avatar/octocat-avatar.png',
},
].map(({ label, avatar }) => (
<Sheet
key={label}
variant="outlined"
className="flex flex-col items-center rounded-[8px] p-4"
>
<Radio value={label.toLowerCase()} variant="soft" className="mb-4" />
<div className="flex h-10 w-10 items-center justify-center overflow-hidden rounded-full">
<img
src={avatar}
className="h-full w-full object-cover"
alt={label}
/>
</div>
<div className="mt-2">{label}</div>
</Sheet>
))}
</RadioGroup>
);
}
No icons
Use the disableIcon
prop to remove the Radio button's icon.
In this case, you'll need to use CSS properties like border and background color to communicate the state of the Radio button, as shown in the demo below:
import { Radio, RadioGroup, Sheet } from 'tailwind-joy/components';
export function RadioGroupNoIcons() {
return (
<RadioGroup
name="radio-group-no-icons"
defaultValue="512GB"
disableIcon
overlay
>
{['512GB', '1TB', '2TB'].map((value) => (
<Sheet
key={value}
variant="outlined"
className="flex rounded-[8px] p-4"
>
<Radio
value={value}
label={`${value} SSD storage`}
className="
[&_.tj-radio-action:has(:checked)]:border-joy-primary-500
font-semibold
[&_.tj-radio-action:has(:checked)]:[--variant-borderWidth:2px]
"
/>
</Sheet>
))}
</RadioGroup>
);
}
Anatomy
The Radio Group component is composed of a root <div>
element that can wrap multiple Radio components.
<div class="tj-radio-group-root ...">
<!-- Radio components here -->
</div>
The Radio component is composed of a root <span>
, with further nested <span>
elements for the radio button, icon, action (with a nested <input>
), and its associated <label>
.
<span class="tj-radio-root ...">
<span class="tj-radio-radio ...">
<span class="tj-radio-icon ..."></span>
<span class="tj-radio-action ...">
<input type="radio" class="tj-radio-input ..." />
</span>
</span>
<label class="tj-radio-label ...">
<!-- label text -->
</label>
</span>
API
See the documentation below for a complete reference to all of the props available to the components mentioned here.