Switch

A toggle switch component for boolean states

Import

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

Usage

import {Label, SwitchControl, SwitchRoot, SwitchThumb} from "@heroui/react";

export function Basic() {
  return (
    <SwitchRoot>
      <SwitchControl>
        <SwitchThumb />
      </SwitchControl>
      <Label className="text-sm">Enable notifications</Label>
    </SwitchRoot>
  );
}

Anatomy

Import the Switch component and access all parts using dot notation.

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

export default () => (
  <Switch>
    <Switch.Control>
      <Switch.Thumb>
        <Switch.Icon/> {/* Optional */}
      </Switch.Thumb>
    </Switch.Control>
    <Label/> {/* Optional */}
  </Switch>
);

For grouping multiple switches, use the SwitchGroup component:

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

export default () => (
  <SwitchGroup>
    <Switch>
      <Switch.Control>
        <Switch.Thumb />
      </Switch.Control>
      <Label>Option 1</Label>
    </Switch>
    <Switch>
      <Switch.Control>
        <Switch.Thumb />
      </Switch.Control>
      <Label>Option 2</Label>
    </Switch>
  </SwitchGroup>
);

Disabled

import {Label, SwitchControl, SwitchRoot, SwitchThumb} from "@heroui/react";

export function Disabled() {
  return (
    <SwitchRoot isDisabled>
      <SwitchControl>
        <SwitchThumb />
      </SwitchControl>
      <Label className="text-sm">Enable notifications</Label>
    </SwitchRoot>
  );
}

Default Selected

import {Label, SwitchControl, SwitchRoot, SwitchThumb} from "@heroui/react";

export function DefaultSelected() {
  return (
    <SwitchRoot defaultSelected>
      <SwitchControl>
        <SwitchThumb />
      </SwitchControl>
      <Label className="text-sm">Enable notifications</Label>
    </SwitchRoot>
  );
}

Controlled

SwitchRoot is off

"use client";

import {Label, SwitchControl, SwitchRoot, SwitchThumb} from "@heroui/react";
import React from "react";

export function Controlled() {
  const [isSelected, setIsSelected] = React.useState(false);

  return (
    <div className="flex flex-col gap-4">
      <SwitchRoot isSelected={isSelected} onChange={setIsSelected}>
        <SwitchControl>
          <SwitchThumb />
        </SwitchControl>
        <Label className="text-sm">Enable notifications</Label>
      </SwitchRoot>
      <p className="text-muted text-sm">SwitchRoot is {isSelected ? "on" : "off"}</p>
    </div>
  );
}

Without Label

import {SwitchControl, SwitchRoot, SwitchThumb} from "@heroui/react";

export function WithoutLabel() {
  return (
    <SwitchRoot aria-label="Enable notifications">
      <SwitchControl>
        <SwitchThumb />
      </SwitchControl>
    </SwitchRoot>
  );
}

Sizes

import {Label, SwitchControl, SwitchRoot, SwitchThumb} from "@heroui/react";

export function Sizes() {
  return (
    <div className="flex gap-6">
      <SwitchRoot size="sm">
        <SwitchControl>
          <SwitchThumb />
        </SwitchControl>
        <Label className="text-xs">Small</Label>
      </SwitchRoot>
      <SwitchRoot size="md">
        <SwitchControl>
          <SwitchThumb />
        </SwitchControl>
        <Label className="text-sm">Medium</Label>
      </SwitchRoot>
      <SwitchRoot size="lg">
        <SwitchControl>
          <SwitchThumb />
        </SwitchControl>
        <Label className="text-base">Large</Label>
      </SwitchRoot>
    </div>
  );
}

Label Position

import {Label, SwitchControl, SwitchRoot, SwitchThumb} from "@heroui/react";

export function LabelPosition() {
  return (
    <div className="flex flex-col gap-4">
      <SwitchRoot>
        <SwitchControl>
          <SwitchThumb />
        </SwitchControl>
        <Label className="text-sm">Label after</Label>
      </SwitchRoot>
      <SwitchRoot>
        <Label className="text-sm">Label before</Label>
        <SwitchControl>
          <SwitchThumb />
        </SwitchControl>
      </SwitchRoot>
    </div>
  );
}

With Icons

"use client";

import {SwitchControl, SwitchIcon, SwitchRoot, SwitchThumb} from "@heroui/react";
import {Icon} from "@iconify/react";

export function WithIcons() {
  const icons = {
    check: {
      off: "gravity-ui:power",
      on: "gravity-ui:check",
      selectedControlClass: "bg-green-500/80",
    },
    darkMode: {
      off: "gravity-ui:moon",
      on: "gravity-ui:sun",
      selectedControlClass: "",
    },
    microphone: {
      off: "gravity-ui:microphone",
      on: "gravity-ui:microphone-slash",
      selectedControlClass: "bg-red-500/80",
    },
    notification: {
      off: "gravity-ui:bell-slash",
      on: "gravity-ui:bell-fill",
      selectedControlClass: "bg-purple-500/80",
    },
    volume: {
      off: "gravity-ui:volume-fill",
      on: "gravity-ui:volume-slash-fill",
      selectedControlClass: "bg-blue-500/80",
    },
  };

  return (
    <div className="flex gap-3">
      {Object.entries(icons).map(([key, value]) => (
        <SwitchRoot key={key} defaultSelected size="lg">
          {({isSelected}) => (
            <>
              <SwitchControl className={isSelected ? value.selectedControlClass : ""}>
                <SwitchThumb>
                  <SwitchIcon>
                    <Icon
                      className={`${isSelected ? "opacity-100" : "opacity-70"} size-3 text-inherit`}
                      icon={isSelected ? value.on : value.off}
                    />
                  </SwitchIcon>
                </SwitchThumb>
              </SwitchControl>
            </>
          )}
        </SwitchRoot>
      ))}
    </div>
  );
}

With Description

import {Description, Label, SwitchControl, SwitchRoot, SwitchThumb} from "@heroui/react";

export function WithDescription() {
  return (
    <div className="max-w-sm">
      <SwitchRoot>
        <div className="flex gap-3">
          <SwitchControl>
            <SwitchThumb />
          </SwitchControl>
          <div className="flex flex-col gap-1">
            <Label className="text-sm">Public profile</Label>
            <Description>Allow others to see your profile information</Description>
          </div>
        </div>
      </SwitchRoot>
    </div>
  );
}

Group

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

export function Group() {
  return (
    <SwitchGroup>
      <Switch name="notifications">
        <Switch.Control>
          <Switch.Thumb />
        </Switch.Control>
        <Label className="text-sm">Allow Notifications</Label>
      </Switch>
      <Switch name="marketing">
        <Switch.Control>
          <Switch.Thumb />
        </Switch.Control>
        <Label className="text-sm">Marketing emails</Label>
      </Switch>
      <Switch name="social">
        <Switch.Control>
          <Switch.Thumb />
        </Switch.Control>
        <Label className="text-sm">Social media updates</Label>
      </Switch>
    </SwitchGroup>
  );
}

Group Horizontal

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

export function GroupHorizontal() {
  return (
    <SwitchGroup className="overflow-x-auto" orientation="horizontal">
      <Switch name="notifications">
        <Switch.Control>
          <Switch.Thumb />
        </Switch.Control>
        <Label className="text-sm">Notifications</Label>
      </Switch>
      <Switch name="marketing">
        <Switch.Control>
          <Switch.Thumb />
        </Switch.Control>
        <Label className="text-sm">Marketing</Label>
      </Switch>
      <Switch name="social">
        <Switch.Control>
          <Switch.Thumb />
        </Switch.Control>
        <Label className="text-sm">Social</Label>
      </Switch>
    </SwitchGroup>
  );
}

Render Props

"use client";

import {Label, SwitchControl, SwitchRoot, SwitchThumb} from "@heroui/react";

export function RenderProps() {
  return (
    <SwitchRoot>
      {({isSelected}) => (
        <>
          <SwitchControl>
            <SwitchThumb />
          </SwitchControl>
          <Label className="text-sm">{isSelected ? "Enabled" : "Disabled"}</Label>
        </>
      )}
    </SwitchRoot>
  );
}

Form Integration

"use client";

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

export function Form() {
  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    const formData = new FormData(e.target as HTMLFormElement);

    alert(
      `Form submitted with:\n${Array.from(formData.entries())
        .map(([key, value]) => `${key}: ${value}`)
        .join("\n")}`,
    );
  };

  return (
    <form className="flex flex-col gap-4" onSubmit={handleSubmit}>
      <SwitchGroup>
        <Switch name="notifications" value="on">
          <Switch.Control>
            <Switch.Thumb />
          </Switch.Control>
          <Label className="text-sm">Enable notifications</Label>
        </Switch>
        <Switch defaultSelected name="newsletter" value="on">
          <Switch.Control>
            <Switch.Thumb />
          </Switch.Control>
          <Label className="text-sm">Subscribe to newsletter</Label>
        </Switch>
        <Switch name="marketing" value="on">
          <Switch.Control>
            <Switch.Thumb />
          </Switch.Control>
          <Label className="text-sm">Receive marketing updates</Label>
        </Switch>
      </SwitchGroup>
      <Button className="mt-4" size="sm" type="submit" variant="primary">
        Submit
      </Button>
    </form>
  );
}

Custom Styles

"use client";

import {SwitchControl, SwitchIcon, SwitchRoot, SwitchThumb} from "@heroui/react";
import {Icon} from "@iconify/react";

export function CustomStyles() {
  return (
    <SwitchRoot>
      {({isSelected}) => (
        <>
          <SwitchControl
            className={`h-[31px] w-[51px] bg-blue-500 ${isSelected ? "bg-cyan-500 shadow-[0_0_12px_rgba(6,182,212,0.5)]" : ""}`}
          >
            <SwitchThumb
              className={`size-[27px] bg-white shadow-sm ${isSelected ? "ms-[22px] shadow-lg" : ""}`}
            >
              <SwitchIcon>
                <Icon
                  className={`size-4 ${isSelected ? "text-cyan-600" : "text-blue-600"}`}
                  icon={isSelected ? "gravity-ui:check" : "gravity-ui:power"}
                />
              </SwitchIcon>
            </SwitchThumb>
          </SwitchControl>
        </>
      )}
    </SwitchRoot>
  );
}

Styling

Passing Tailwind CSS classes

You can customize individual Switch components:

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

function CustomSwitch() {
  return (
    <Switch>
      {({isSelected}) => (
        <>
          <Switch.Control
            className={`h-[31px] w-[51px] bg-blue-500 ${isSelected ? "bg-cyan-500 shadow-[0_0_12px_rgba(6,182,212,0.5)]" : ""}`}
          >
            <Switch.Thumb
              className={`size-[27px] bg-white shadow-sm ${isSelected ? "translate-x-5 shadow-lg" : ""}`}
            />
          </Switch.Control>
          <Label>Custom Switch</Label>
        </>
      )}
    </Switch>
  );
}

Or customize the SwitchGroup layout:

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

function CustomSwitchGroup() {
  return (
    <SwitchGroup className="gap-8" orientation="horizontal">
      <Switch>
        <Switch.Control>
          <Switch.Thumb />
        </Switch.Control>
        <Label>Option 1</Label>
      </Switch>
      <Switch>
        <Switch.Control>
          <Switch.Thumb />
        </Switch.Control>
        <Label>Option 2</Label>
      </Switch>
    </SwitchGroup>
  );
}

Customizing the component classes

To customize the Switch component classes, you can use the @layer components directive.
Learn more.

@layer components {
  .switch {
    @apply inline-flex gap-3 items-center;
  }

  .switch__control {
    @apply h-5 w-8 bg-gray-400 data-[selected=true]:bg-blue-500;
  }

  .switch__thumb {
    @apply bg-white shadow-sm;
  }

  .switch__icon {
    @apply h-3 w-3 text-current;
  }
}

HeroUI follows the BEM methodology to ensure component variants and states are reusable and easy to customize.

CSS Classes

Switch Classes

The Switch component uses these CSS classes (View source styles):

  • .switch - Base switch container
  • .switch__control - Switch control track
  • .switch__thumb - Switch thumb that moves
  • .switch__icon - Optional icon inside the thumb
  • .switch--sm - Small size variant
  • .switch--md - Medium size variant (default)
  • .switch--lg - Large size variant

SwitchGroup Classes

The SwitchGroup component uses these CSS classes (View source styles):

  • .switch-group - Switch group container
  • .switch-group__items - Container for switch items
  • .switch-group--horizontal - Horizontal layout
  • .switch-group--vertical - Vertical layout (default)

Interactive States

The switch supports both CSS pseudo-classes and data attributes for flexibility:

  • Selected: [data-selected="true"] (thumb position and background color change)
  • Hover: :hover or [data-hovered="true"]
  • Focus: :focus-visible or [data-focus-visible="true"] (shows focus ring)
  • Disabled: :disabled or [aria-disabled="true"] (reduced opacity, no pointer events)
  • Pressed: :active or [data-pressed="true"]

API Reference

Switch Props

Inherits from React Aria Switch.

PropTypeDefaultDescription
size'sm' | 'md' | 'lg''md'The size of the switch
isSelectedbooleanfalseWhether the switch is on
defaultSelectedbooleanfalseWhether the switch is on by default (uncontrolled)
isDisabledbooleanfalseWhether the switch is disabled
namestring-The name of the input element, used when submitting an HTML form
valuestring-The value of the input element, used when submitting an HTML form
onChange(isSelected: boolean) => void-Handler called when the switch value changes
onPress(e: PressEvent) => void-Handler called when the switch is pressed
childrenReact.ReactNode | (values: SwitchRenderProps) => React.ReactNode-Switch content or render prop

SwitchRenderProps

When using the render prop pattern, these values are provided:

PropTypeDescription
isSelectedbooleanWhether the switch is currently on
isHoveredbooleanWhether the switch is hovered
isPressedbooleanWhether the switch is currently pressed
isFocusedbooleanWhether the switch is focused
isFocusVisiblebooleanWhether the switch is keyboard focused
isDisabledbooleanWhether the switch is disabled
isReadOnlybooleanWhether the switch is read only
state-State of the switch.

SwitchGroup Props

PropTypeDefaultDescription
orientation'horizontal' | 'vertical''vertical'The orientation of the switch group
childrenReact.ReactNode-The switch items to render
classNamestring-Additional CSS class names