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.
Overview
The Checkbox component in HeroUI v3 has been redesigned with a compound component pattern, requiring explicit structure with Checkbox.Control, Checkbox.Indicator, and Checkbox.Content components.
Structure Changes
v2: Simple Component with Children as Label
In v2, Checkbox accepted children as the label:
import { Checkbox } from "@heroui/react";
<Checkbox defaultSelected>Option</Checkbox>v3: Compound Component Structure
In v3, Checkbox uses a compound component pattern with explicit subcomponents:
import { Checkbox, Label } from "@heroui/react";
<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. Event Handler: onValueChange → onChange
v2: Used onValueChange prop
v3: Uses onChange prop (from React Aria Components)
4. Removed Props
The following props are no longer available in v3:
color- Use Tailwind CSS classes onCheckbox.Controlfor custom colorssize- Use Tailwind CSS classes likesize-4,size-5,size-6radius- Use Tailwind CSS classes likerounded-sm,rounded-md,rounded-fulllineThrough- Use Tailwind CSS classline-throughon labelicon- Use custom content inCheckbox.IndicatorinsteadclassNames- UseclassNameprops on individual componentsdisableAnimation- Animations are handled internally
5. CheckboxGroup Changes
v2: Checkbox.Group (compound component)
v3: CheckboxGroup (separate component, but structure of children changed)
Migration Examples
Basic Usage
import { Checkbox } from "@heroui/react";
export default function App() {
return <Checkbox defaultSelected>Option</Checkbox>;
}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>
);
}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 } from "@heroui/react";
<Checkbox.Group label="Select cities">
<Checkbox value="buenos-aires">Buenos Aires</Checkbox>
<Checkbox value="sydney">Sydney</Checkbox>
<Checkbox value="san-francisco">San Francisco</Checkbox>
</Checkbox.Group>import { CheckboxGroup, 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
<Checkbox defaultSelected color="primary">Primary</Checkbox>
<Checkbox defaultSelected color="success">Success</Checkbox>
<Checkbox defaultSelected color="danger">Danger</Checkbox>{/* Use Tailwind classes for custom colors */}
<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
<Checkbox defaultSelected size="sm">Small</Checkbox>
<Checkbox defaultSelected size="md">Medium</Checkbox>
<Checkbox defaultSelected size="lg">Large</Checkbox>{/* Use Tailwind classes for sizes */}
<Checkbox defaultSelected id="small">
<Checkbox.Control className="size-4">
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="small">Small</Label>
</Checkbox.Content>
</Checkbox>
<Checkbox defaultSelected id="medium">
<Checkbox.Control className="size-5">
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="medium">Medium</Label>
</Checkbox.Content>
</Checkbox>
<Checkbox defaultSelected id="large">
<Checkbox.Control className="size-6">
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="large">Large</Label>
</Checkbox.Content>
</Checkbox>Radius
<Checkbox defaultSelected radius="full">Rounded</Checkbox>
<Checkbox defaultSelected radius="lg">Large Radius</Checkbox><Checkbox defaultSelected id="rounded">
<Checkbox.Control className="rounded-full">
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="rounded">Rounded</Label>
</Checkbox.Content>
</Checkbox>
<Checkbox defaultSelected id="large-radius">
<Checkbox.Control className="rounded-lg">
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="large-radius">Large Radius</Label>
</Checkbox.Content>
</Checkbox>Line Through
<Checkbox defaultSelected lineThrough>
Option
</Checkbox><Checkbox defaultSelected id="option">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="option" className="line-through">
Option
</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>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>Disabled State
<Checkbox isDisabled>Disabled</Checkbox><Checkbox isDisabled id="disabled">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="disabled">Disabled</Label>
</Checkbox.Content>
</Checkbox>Invalid State
<Checkbox isInvalid>Invalid</Checkbox><Checkbox isInvalid id="invalid">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="invalid">Invalid</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>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)Breaking Changes 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: Changed from
Checkbox.GrouptoCheckboxGroupwith different child structure - ClassNames Removed: Use
classNameprops on individual components
Tips for Migration
- Start with structure: Convert to compound components first
- Add Label component: Wrap labels in
Checkbox.ContentwithLabelcomponent - Use htmlFor/id: Always provide
idon Checkbox andhtmlForon Label for accessibility - Custom styling: Use Tailwind classes on
Checkbox.Controlfor colors, sizes, and radius - Custom indicators: Use render props in
Checkbox.Indicatorfor custom icons - CheckboxGroup: Update from
Checkbox.GrouptoCheckboxGroupand restructure children
Need Help?
For v3 Checkbox features and API:
- See the API Reference
- Check interactive examples
For community support: