27.5k

Switch

Migration guide for Switch from HeroUI v2 to v3

Refer to the v3 Switch documentation for complete API reference, styling guide, and advanced examples. This guide only focuses on migrating from HeroUI v2.

Overview

The Switch component in HeroUI v3 has been redesigned with a compound component pattern, requiring explicit structure with Switch.Control and Switch.Thumb components.

Structure Changes

v2: Simple Structure

In v2, Switch used a simple structure with children as label:

import { Switch } from "@heroui/react";

<Switch>Enable notifications</Switch>

v3: Compound Components

In v3, Switch requires compound components:

import { Switch, Label } from "@heroui/react";

<Switch>
  <Switch.Control>
    <Switch.Thumb />
  </Switch.Control>
  <Label>Enable notifications</Label>
</Switch>

Key Changes

1. Component Structure

v2: Simple Switch with children as label
v3: Compound components (Switch.Control, Switch.Thumb) with Label component

2. Prop Changes

v2 Propv3 PropNotes
onValueChangeonChangeRenamed event handler
label-Removed (use Label component)
color-Removed (use Tailwind CSS)
thumbIcon-Removed (use Switch.Icon inside Switch.Thumb)
startContent-Removed (customize control)
endContent-Removed (customize control)
classNames-Use className props
disableAnimation-Removed

3. Removed Props

The following props are no longer available in v3:

  • label - Use Label component instead
  • color - Use Tailwind CSS classes
  • thumbIcon - Use Switch.Icon component inside Switch.Thumb
  • startContent, endContent - Customize control directly
  • classNames - Use className props on individual components
  • disableAnimation - Animations handled differently

4. New Components

  • SwitchGroup - For grouping multiple switches
  • Switch.Icon - For icons inside the thumb

Migration Examples

Basic Usage

import { Switch } from "@heroui/react";

export default function App() {
  return <Switch defaultSelected aria-label="Automatic updates" />;
}
import { Switch, Label } from "@heroui/react";

export default function App() {
  return (
    <Switch defaultSelected>
      <Switch.Control>
        <Switch.Thumb />
      </Switch.Control>
      <Label>Automatic updates</Label>
    </Switch>
  );
}

Controlled Switch

import { useState } from "react";

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

<Switch isSelected={isSelected} onValueChange={setIsSelected}>
  Airplane mode
</Switch>
import { useState } from "react";

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

<Switch isSelected={isSelected} onChange={setIsSelected}>
  <Switch.Control>
    <Switch.Thumb />
  </Switch.Control>
  <Label>Airplane mode</Label>
</Switch>

Without Label

<Switch defaultSelected aria-label="Automatic updates" />
<Switch defaultSelected aria-label="Automatic updates">
  <Switch.Control>
    <Switch.Thumb />
  </Switch.Control>
</Switch>

With Thumb Icon

<Switch thumbIcon={<CheckIcon />}>Enable notifications</Switch>
<Switch>
  <Switch.Control>
    <Switch.Thumb>
      <Switch.Icon>
        <CheckIcon />
      </Switch.Icon>
    </Switch.Thumb>
  </Switch.Control>
  <Label>Enable notifications</Label>
</Switch>

With Start/End Content

<Switch
  startContent={<SunIcon />}
  endContent={<MoonIcon />}
>
  Dark mode
</Switch>
<Switch>
  <Switch.Control className="flex items-center gap-2">
    <SunIcon />
    <Switch.Thumb />
    <MoonIcon />
  </Switch.Control>
  <Label>Dark mode</Label>
</Switch>

Sizes

<div className="flex gap-4">
  <Switch size="sm">Small</Switch>
  <Switch size="md">Medium</Switch>
  <Switch size="lg">Large</Switch>
</div>
<div className="flex gap-4">
  <Switch size="sm">
    <Switch.Control>
      <Switch.Thumb />
    </Switch.Control>
    <Label>Small</Label>
  </Switch>
  <Switch size="md">
    <Switch.Control>
      <Switch.Thumb />
    </Switch.Control>
    <Label>Medium</Label>
  </Switch>
  <Switch size="lg">
    <Switch.Control>
      <Switch.Thumb />
    </Switch.Control>
    <Label>Large</Label>
  </Switch>
</div>

Colors

<Switch color="primary">Primary</Switch>
<Switch color="success">Success</Switch>
<Switch color="danger">Danger</Switch>
{/* Use Tailwind CSS classes for colors */}
<Switch>
  <Switch.Control className="data-[selected=true]:bg-accent">
    <Switch.Thumb />
  </Switch.Control>
  <Label>Primary</Label>
</Switch>
<Switch>
  <Switch.Control className="data-[selected=true]:bg-success">
    <Switch.Thumb />
  </Switch.Control>
  <Label>Success</Label>
</Switch>
<Switch>
  <Switch.Control className="data-[selected=true]:bg-danger">
    <Switch.Thumb />
  </Switch.Control>
  <Label>Danger</Label>
</Switch>

Disabled State

<Switch isDisabled>Disabled switch</Switch>
<Switch isDisabled>
  <Switch.Control>
    <Switch.Thumb />
  </Switch.Control>
  <Label>Disabled switch</Label>
</Switch>

Custom Styling

<Switch
  classNames={{
    base: "custom-base",
    wrapper: "custom-wrapper",
    thumb: "custom-thumb"
  }}
>
  Custom switch
</Switch>
<Switch className="custom-base">
  <Switch.Control className="custom-control">
    <Switch.Thumb className="custom-thumb" />
  </Switch.Control>
  <Label>Custom switch</Label>
</Switch>

Switch Group

{/* No built-in group component in v2 */}
<div className="flex flex-col gap-2">
  <Switch name="notifications">Allow Notifications</Switch>
  <Switch name="marketing">Marketing emails</Switch>
</div>
import { SwitchGroup } from "@heroui/react";

<SwitchGroup>
  <Switch name="notifications">
    <Switch.Control>
      <Switch.Thumb />
    </Switch.Control>
    <Label>Allow Notifications</Label>
  </Switch>
  <Switch name="marketing">
    <Switch.Control>
      <Switch.Thumb />
    </Switch.Control>
    <Label>Marketing emails</Label>
  </Switch>
</SwitchGroup>

Component Anatomy

The v3 Switch follows this structure:

Switch (Root)
  ├── Switch.Control
  │   └── Switch.Thumb
  │       └── Switch.Icon (optional)
  └── Label (optional)

For groups:

SwitchGroup
  ├── Switch
  │   ├── Switch.Control
  │   │   └── Switch.Thumb
  │   └── Label
  └── Switch
      ├── Switch.Control
      │   └── Switch.Thumb
      └── Label

Important Notes

Event Handler

  • v2: onValueChange prop
  • v3: onChange prop (same signature: (isSelected: boolean) => void)

Label Handling

  • v2: Children were used as label
  • v3: Use Label component separately

Icons

  • v2: thumbIcon prop for icon inside thumb, startContent/endContent for icons outside
  • v3: Use Switch.Icon inside Switch.Thumb for thumb icon, customize Switch.Control for start/end content

Breaking Changes Summary

  1. Component Structure: Must use compound components (Switch.Control, Switch.Thumb)
  2. Label Handling: Children no longer used as label - use Label component
  3. Event Handler: onValueChangeonChange
  4. Styling Props Removed: color - use Tailwind CSS
  5. Icon Props Removed: thumbIcon, startContent, endContent - use components or customize directly
  6. ClassNames Removed: Use className props on individual components
  7. New Component: SwitchGroup available for grouping switches

Tips for Migration

  1. Update structure: Wrap Switch content in Switch.Control and Switch.Thumb
  2. Add Label: Use Label component for switch label
  3. Update event handler: Change onValueChange to onChange
  4. Update icons: Replace thumbIcon with Switch.Icon inside Switch.Thumb
  5. Update start/end content: Add icons directly to Switch.Control with appropriate styling
  6. Update colors: Use Tailwind CSS classes with data-[selected=true]: prefix
  7. Update styling: Replace classNames prop with className props on individual components
  8. Use SwitchGroup: For grouping switches, use SwitchGroup component

Need Help?

For v3 Switch features and API:

For community support: