Checkbox
Migration guide for Checkbox from HeroUI v2 to v3
Refer to the v3 Checkbox documentation for complete API reference, styling guide, and advanced examples. This guide only focuses on migrating from HeroUI v2.
Structure Changes
In v2, Checkbox accepted children as the label:
import { Checkbox } from "@heroui/react";
export default function App() {
return <Checkbox defaultSelected>Option</Checkbox>;
}In v3, Checkbox uses a compound component pattern with explicit subcomponents:
import { Checkbox, Label } from "@heroui/react";
export default function App() {
return (
<Checkbox defaultSelected id="option">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="option">Option</Label>
</Checkbox.Content>
</Checkbox>
);
}Key Changes
1. Component Structure
v2: Simple component with children as label
v3: Compound components: Checkbox.Control, Checkbox.Indicator, Checkbox.Content
2. Label Handling
v2: Label passed as children directly to Checkbox
v3: Label must be wrapped in Checkbox.Content using the Label component
3. Prop Changes
| v2 Prop | v3 Location | Notes |
|---|---|---|
onValueChange | Checkbox | Use onChange |
color | - | Removed (use Tailwind on Checkbox.Control) |
size | - | Removed (use Tailwind e.g. size-4, size-5) |
radius | - | Removed (use Tailwind e.g. rounded-sm) |
lineThrough | - | Removed (use Tailwind line-through on label) |
icon | - | Use custom content in Checkbox.Indicator |
classNames | - | Use className on parts |
disableAnimation | - | Removed |
| — | variant | New prop: "primary" (default) or "secondary" for lower emphasis styling |
4. CheckboxGroup Changes
v2: CheckboxGroup (separate component) with label prop and simple Checkbox children
v3: CheckboxGroup (separate component) with Label/Description as children and compound Checkbox structure
For migrating checkbox groups, see the CheckboxGroup migration guide.
Migration Examples
Controlled Checkbox
import { useState } from "react";
const [isSelected, setIsSelected] = useState(false);
<Checkbox isSelected={isSelected} onValueChange={setIsSelected}>
Subscribe
</Checkbox>import { useState } from "react";
import { Label } from "@heroui/react";
const [isSelected, setIsSelected] = useState(false);
<Checkbox id="subscribe" isSelected={isSelected} onChange={setIsSelected}>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="subscribe">Subscribe</Label>
</Checkbox.Content>
</Checkbox>Checkbox with Description
{/* v2 doesn't have built-in description support */}
<Checkbox defaultSelected>
Option
</Checkbox>
<p className="text-sm text-default-500">Description text</p>import { Checkbox, Label, Description } from "@heroui/react";
<Checkbox id="option" defaultSelected>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="option">Option</Label>
<Description>Description text</Description>
</Checkbox.Content>
</Checkbox>CheckboxGroup
import { Checkbox, CheckboxGroup } from "@heroui/react";
<CheckboxGroup label="Select cities">
<Checkbox value="buenos-aires">Buenos Aires</Checkbox>
<Checkbox value="sydney">Sydney</Checkbox>
<Checkbox value="san-francisco">San Francisco</Checkbox>
</CheckboxGroup>import { CheckboxGroup, Checkbox, Label, Description } from "@heroui/react";
<CheckboxGroup name="cities">
<Label>Select cities</Label>
<Description>Choose all that apply</Description>
<Checkbox value="buenos-aires">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Buenos Aires</Label>
</Checkbox.Content>
</Checkbox>
<Checkbox value="sydney">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Sydney</Label>
</Checkbox.Content>
</Checkbox>
<Checkbox value="san-francisco">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>San Francisco</Label>
</Checkbox.Content>
</Checkbox>
</CheckboxGroup>Colors and Sizes
{/* Colors */}
<Checkbox defaultSelected color="primary">Primary</Checkbox>
{/* Sizes */}
<Checkbox defaultSelected size="md">Medium</Checkbox>{/* Colors - use Tailwind classes */}
<Checkbox defaultSelected id="primary">
<Checkbox.Control className="data-[selected=true]:bg-primary data-[selected=true]:border-primary">
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="primary">Primary</Label>
</Checkbox.Content>
</Checkbox>
{/* Sizes - use Tailwind classes */}
<Checkbox defaultSelected id="medium">
<Checkbox.Control className="size-5">
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="medium">Medium</Label>
</Checkbox.Content>
</Checkbox>Custom Icon/Indicator
<Checkbox defaultSelected icon={<HeartIcon />}>
Option
</Checkbox><Checkbox defaultSelected id="option">
<Checkbox.Control>
<Checkbox.Indicator>
{({isSelected}) => isSelected ? <HeartIcon /> : null}
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="option">Option</Label>
</Checkbox.Content>
</Checkbox>Variants
v3 introduces a variant prop with "primary" (default) and "secondary" options:
{/* Primary variant (default) */}
<Checkbox variant="primary" id="primary-option">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="primary-option">Primary</Label>
</Checkbox.Content>
</Checkbox>
{/* Secondary variant - lower emphasis, suitable for Surface components */}
<Checkbox variant="secondary" id="secondary-option">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="secondary-option">Secondary</Label>
</Checkbox.Content>
</Checkbox>Indeterminate State
<Checkbox isIndeterminate>Option</Checkbox><Checkbox isIndeterminate id="option">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="option">Option</Label>
</Checkbox.Content>
</Checkbox>Render Props Pattern
v3 Checkbox supports a render prop pattern that provides state information:
<Checkbox id="terms">
{({isSelected, isIndeterminate, isHovered, isPressed, isFocused, isDisabled}) => (
<>
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="terms">
{isSelected ? "Terms accepted" : "Accept terms"}
</Label>
</Checkbox.Content>
</>
)}
</Checkbox>Checkbox.Indicator also supports a render prop pattern, providing the same state values. This is useful for rendering custom indicator content based on the checkbox state:
<Checkbox id="custom">
<Checkbox.Control>
<Checkbox.Indicator>
{({isSelected, isIndeterminate}) =>
isIndeterminate ? <MinusIcon /> : isSelected ? <CheckIcon /> : null
}
</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="custom">Custom indicator</Label>
</Checkbox.Content>
</Checkbox>Available render props:
isSelected- Whether checkbox is checkedisIndeterminate- Whether checkbox is in indeterminate stateisHovered- Whether checkbox is hoveredisPressed- Whether checkbox is currently pressedisFocused- Whether checkbox is focusedisFocusVisible- Whether checkbox should show focus indicatorisDisabled- Whether checkbox is disabledisReadOnly- Whether checkbox is read only
Styling Changes
v2: classNames Prop
<Checkbox
classNames={{
base: "custom-base",
wrapper: "custom-wrapper",
icon: "custom-icon",
label: "custom-label"
}}
/>v3: Direct className Props
<Checkbox className="custom-base" id="option">
<Checkbox.Control className="custom-control">
<Checkbox.Indicator className="custom-indicator" />
</Checkbox.Control>
<Checkbox.Content className="custom-content">
<Label htmlFor="option" className="custom-label">
Option
</Label>
</Checkbox.Content>
</Checkbox>Component Anatomy
The v3 Checkbox follows this structure:
Checkbox (Root)
├── Checkbox.Control
│ └── Checkbox.Indicator
└── Checkbox.Content (optional)
├── Label (required if using Content)
└── Description (optional)Summary
- Component Structure: Must use compound components (
Control,Indicator,Content) - Label Handling: Labels must use
Labelcomponent insideCheckbox.Content - onValueChange → onChange: Event handler prop renamed
- Color Removed: Use Tailwind CSS classes on
Checkbox.Control - Size Removed: Use Tailwind CSS classes on
Checkbox.Control - Radius Removed: Use Tailwind CSS classes on
Checkbox.Control - LineThrough Removed: Use Tailwind
line-throughclass on label - Icon Prop Removed: Use custom content in
Checkbox.Indicator - CheckboxGroup: Same component name; use
Label/Descriptionas children and compoundCheckboxstructure - ClassNames Removed: Use
classNameprops on individual components - New
variantProp: Supports"primary"(default) and"secondary"for lower emphasis styling - Indicator Render Props:
Checkbox.Indicatoraccepts a render function with state values (isSelected,isIndeterminate, etc.)