Skip to main content

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'.

Select an option and use keyboard ↑↓.
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 &uarr;&darr;.
</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:

Try using keyboard navigation.
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'.

Select an option and use keyboard ↑↓.
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 &uarr;&darr;.
</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.

Tailwind
Tailwind
Joy
Joy
Octocat
Octocat
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.