ProComponents, templates & AI tooling
HeroUI
27.7k

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.Content>
        <Checkbox.Control>
          <Checkbox.Indicator />
        </Checkbox.Control>
        Option
      </Checkbox.Content>
    </Checkbox>
  );
}

Key Changes

1. Component Structure

v2: Simple component with children as label
v3: Compound components: Checkbox.Content, Checkbox.Control, Checkbox.Indicator

2. Label Handling

v2: Label passed as children directly to Checkbox
v3: Label goes inside Checkbox.Content (the clickable label); Description/FieldError are siblings of Checkbox.Content

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

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.Content>
    <Checkbox.Control>
      <Checkbox.Indicator />
    </Checkbox.Control>
    Subscribe
  </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.Content>
    <Checkbox.Control>
      <Checkbox.Indicator />
    </Checkbox.Control>
    Option
  </Checkbox.Content>
  <Description>Description text</Description>
</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.Content>
      <Checkbox.Control>
        <Checkbox.Indicator />
      </Checkbox.Control>
      Buenos Aires
    </Checkbox.Content>
  </Checkbox>
  <Checkbox value="sydney">
    <Checkbox.Content>
      <Checkbox.Control>
        <Checkbox.Indicator />
      </Checkbox.Control>
      Sydney
    </Checkbox.Content>
  </Checkbox>
  <Checkbox value="san-francisco">
    <Checkbox.Content>
      <Checkbox.Control>
        <Checkbox.Indicator />
      </Checkbox.Control>
      San Francisco
    </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.Content>
    <Checkbox.Control className="data-[selected=true]:bg-primary data-[selected=true]:border-primary">
      <Checkbox.Indicator />
    </Checkbox.Control>
    Primary
  </Checkbox.Content>
</Checkbox>

{/* Sizes - use Tailwind classes */}
<Checkbox defaultSelected id="medium">
  <Checkbox.Content>
    <Checkbox.Control className="size-5">
      <Checkbox.Indicator />
    </Checkbox.Control>
    Medium
  </Checkbox.Content>
</Checkbox>

Custom Icon/Indicator

<Checkbox defaultSelected icon={<HeartIcon />}>
  Option
</Checkbox>
<Checkbox defaultSelected id="option">
  <Checkbox.Content>
    <Checkbox.Control>
      <Checkbox.Indicator>
        {({isSelected}) => isSelected ? <HeartIcon /> : null}
      </Checkbox.Indicator>
    </Checkbox.Control>
    Option
  </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.Content>
    <Checkbox.Control>
      <Checkbox.Indicator />
    </Checkbox.Control>
    Primary
  </Checkbox.Content>
</Checkbox>

{/* Secondary variant - lower emphasis, suitable for Surface components */}
<Checkbox variant="secondary" id="secondary-option">
  <Checkbox.Content>
    <Checkbox.Control>
      <Checkbox.Indicator />
    </Checkbox.Control>
    Secondary
  </Checkbox.Content>
</Checkbox>

Indeterminate State

<Checkbox isIndeterminate>Option</Checkbox>
<Checkbox isIndeterminate id="option">
  <Checkbox.Content>
    <Checkbox.Control>
      <Checkbox.Indicator />
    </Checkbox.Control>
    Option
  </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.Content>
        <Checkbox.Control>
          <Checkbox.Indicator />
        </Checkbox.Control>
        
          {isSelected ? "Terms accepted" : "Accept terms"}
        
      </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.Content>
    <Checkbox.Control>
      <Checkbox.Indicator>
        {({isSelected, isIndeterminate}) =>
          isIndeterminate ? <MinusIcon /> : isSelected ? <CheckIcon /> : null
        }
      </Checkbox.Indicator>
    </Checkbox.Control>
    Custom indicator
  </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

<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.Content>
    <Checkbox.Control className="custom-control">
      <Checkbox.Indicator className="custom-indicator" />
    </Checkbox.Control>
    
      Option
    
  </Checkbox.Content>
</Checkbox>

Component Anatomy

The v3 Checkbox follows this structure:

Checkbox (Root)
  ├── Checkbox.Content (the clickable label)
  │   ├── Checkbox.Control
  │   │   └── Checkbox.Indicator
  │   └── Label
  ├── Description (optional, sibling)
  └── FieldError (optional, sibling)

Summary

  1. Component Structure: Must use compound components (Button, Control, Indicator)
  2. Label Handling: Labels go 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.)

On this page