apps/docs/content/docs/react/migration/(components)/checkbox.mdx
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>
);
}
v2: Simple component with children as label
v3: Compound components: Checkbox.Control, Checkbox.Indicator, Checkbox.Content
v2: Label passed as children directly to Checkbox
v3: Label must be wrapped in Checkbox.Content using the Label component
| 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 |
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.
<Tabs items={["v2", "v3"]}> <Tab value="v2"> ```tsx import { useState } from "react";
const [isSelected, setIsSelected] = useState(false);
<Checkbox isSelected={isSelected} onValueChange={setIsSelected}>
Subscribe
</Checkbox>
```
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>
```
<Tabs items={["v2", "v3"]}>
<Tab value="v2">
tsx <Checkbox defaultSelected> Option </Checkbox> <p className="text-sm text-default-500">Description text</p>
</Tab>
<Tab value="v3">
```tsx
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>
```
<Tabs items={["v2", "v3"]}> <Tab value="v2"> ```tsx 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>
```
<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>
```
<Tabs items={["v2", "v3"]}>
<Tab value="v2">
tsx <Checkbox defaultSelected color="primary">Primary</Checkbox> <Checkbox defaultSelected size="md">Medium</Checkbox>
</Tab>
<Tab value="v3">
tsx <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> <Checkbox defaultSelected id="medium"> <Checkbox.Control className="size-5"> <Checkbox.Indicator /> </Checkbox.Control> <Checkbox.Content> <Label htmlFor="medium">Medium</Label> </Checkbox.Content> </Checkbox>
</Tab>
</Tabs>
<Tabs items={["v2", "v3"]}>
<Tab value="v2">
tsx <Checkbox defaultSelected icon={<HeartIcon />}> Option </Checkbox>
</Tab>
<Tab value="v3">
tsx <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>
</Tab>
</Tabs>
v3 introduces a variant prop with "primary" (default) and "secondary" options:
<Checkbox variant="primary" id="primary-option">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="primary-option">Primary</Label>
</Checkbox.Content>
</Checkbox>
<Checkbox variant="secondary" id="secondary-option">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label htmlFor="secondary-option">Secondary</Label>
</Checkbox.Content>
</Checkbox>
<Tabs items={["v2", "v3"]}>
<Tab value="v2">
tsx <Checkbox isIndeterminate>Option</Checkbox>
</Tab>
<Tab value="v3">
tsx <Checkbox isIndeterminate id="option"> <Checkbox.Control> <Checkbox.Indicator /> </Checkbox.Control> <Checkbox.Content> <Label htmlFor="option">Option</Label> </Checkbox.Content> </Checkbox>
</Tab>
</Tabs>
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 onlyclassNames Prop<Checkbox
classNames={{
base: "custom-base",
wrapper: "custom-wrapper",
icon: "custom-icon",
label: "custom-label"
}}
/>
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>
The v3 Checkbox follows this structure:
Checkbox (Root)
├── Checkbox.Control
│ └── Checkbox.Indicator
└── Checkbox.Content (optional)
├── Label (required if using Content)
└── Description (optional)
Control, Indicator, Content)Label component inside Checkbox.ContentCheckbox.ControlCheckbox.ControlCheckbox.Controlline-through class on labelCheckbox.IndicatorLabel/Description as children and compound Checkbox structureclassName props on individual componentsvariant Prop: Supports "primary" (default) and "secondary" for lower emphasis stylingCheckbox.Indicator accepts a render function with state values (isSelected, isIndeterminate, etc.)