Back to Heroui

Checkbox

apps/docs/content/docs/react/migration/(components)/checkbox.mdx

3.0.510.6 KB
Original Source
<Callout type="info"> Refer to the [v3 Checkbox documentation](/docs/react/components/checkbox) for complete API reference, styling guide, and advanced examples. This guide only focuses on migrating from HeroUI v2. </Callout>

Structure Changes

In v2, Checkbox accepted children as the label:

tsx
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:

tsx
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 Propv3 LocationNotes
onValueChangeCheckboxUse 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
variantNew 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

<Tabs items={["v2", "v3"]}> <Tab value="v2"> ```tsx import { useState } from "react";

const [isSelected, setIsSelected] = useState(false);

<Checkbox isSelected={isSelected} onValueChange={setIsSelected}>
  Subscribe
</Checkbox>
```
</Tab> <Tab value="v3"> ```tsx 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>
```
</Tab> </Tabs>

Checkbox with Description

<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>
```
</Tab> </Tabs>

CheckboxGroup

<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>
```
</Tab> <Tab value="v3"> ```tsx 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>
```
</Tab> </Tabs>

Colors and Sizes

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

Custom Icon/Indicator

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

Variants

v3 introduces a variant prop with "primary" (default) and "secondary" options:

tsx
<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>

Indeterminate State

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

Render Props Pattern

v3 Checkbox supports a render prop pattern that provides state information:

tsx
<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:

tsx
<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 checked
  • isIndeterminate - Whether checkbox is in indeterminate state
  • isHovered - Whether checkbox is hovered
  • isPressed - Whether checkbox is currently pressed
  • isFocused - Whether checkbox is focused
  • isFocusVisible - Whether checkbox should show focus indicator
  • isDisabled - Whether checkbox is disabled
  • isReadOnly - Whether checkbox is read only

Styling Changes

v2: classNames Prop

tsx
<Checkbox 
  classNames={{
    base: "custom-base",
    wrapper: "custom-wrapper",
    icon: "custom-icon",
    label: "custom-label"
  }}
/>

v3: Direct className Props

tsx
<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

  1. Component Structure: Must use compound components (Control, Indicator, Content)
  2. Label Handling: Labels must use Label component inside Checkbox.Content
  3. onValueChange → onChange: Event handler prop renamed
  4. Color Removed: Use Tailwind CSS classes on Checkbox.Control
  5. Size Removed: Use Tailwind CSS classes on Checkbox.Control
  6. Radius Removed: Use Tailwind CSS classes on Checkbox.Control
  7. LineThrough Removed: Use Tailwind line-through class on label
  8. Icon Prop Removed: Use custom content in Checkbox.Indicator
  9. CheckboxGroup: Same component name; use Label/Description as children and compound Checkbox structure
  10. ClassNames Removed: Use className props on individual components
  11. New variant Prop: Supports "primary" (default) and "secondary" for lower emphasis styling
  12. Indicator Render Props: Checkbox.Indicator accepts a render function with state values (isSelected, isIndeterminate, etc.)