SwitchUpdated
A toggle switch component for boolean states
Import
import { Switch, SwitchGroup, Label } from '@heroui/react';Usage
"use client";
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.Root>
<Switch.Control>
<Switch.Thumb>
<Switch.Icon/> {/* Optional */}
</Switch.Thumb>
</Switch.Control>
<Label/> {/* Optional */}
</Switch.Root>
);For grouping multiple switches, use the SwitchGroup component:
import { Switch, SwitchGroup, Label } from '@heroui/react';
export default () => (
<SwitchGroup>
<Switch.Root>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label>Option 1</Label>
</Switch.Root>
<Switch.Root>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label>Option 2</Label>
</Switch.Root>
</SwitchGroup>
);Disabled
"use client";
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
"use client";
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
"use client";
import {SwitchControl, SwitchRoot, SwitchThumb} from "@heroui/react";
export function WithoutLabel() {
return (
<SwitchRoot aria-label="Enable notifications">
<SwitchControl>
<SwitchThumb />
</SwitchControl>
</SwitchRoot>
);
}Sizes
"use client";
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
"use client";
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
"use client";
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
"use client";
import {Label, Switch, SwitchGroup} from "@heroui/react";
export function Group() {
return (
<SwitchGroup>
<Switch.Root name="notifications">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm">Allow Notifications</Label>
</Switch.Root>
<Switch.Root name="marketing">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm">Marketing emails</Label>
</Switch.Root>
<Switch.Root name="social">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm">Social media updates</Label>
</Switch.Root>
</SwitchGroup>
);
}Group Horizontal
"use client";
import {Label, Switch, SwitchGroup} from "@heroui/react";
export function GroupHorizontal() {
return (
<SwitchGroup className="overflow-x-auto" orientation="horizontal">
<Switch.Root name="notifications">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm">Notifications</Label>
</Switch.Root>
<Switch.Root name="marketing">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm">Marketing</Label>
</Switch.Root>
<Switch.Root name="social">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm">Social</Label>
</Switch.Root>
</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.Root name="notifications" value="on">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm">Enable notifications</Label>
</Switch.Root>
<Switch.Root defaultSelected name="newsletter" value="on">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm">Subscribe to newsletter</Label>
</Switch.Root>
<Switch.Root name="marketing" value="on">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm">Receive marketing updates</Label>
</Switch.Root>
</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.Root>
{({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.Root>
);
}Or customize the SwitchGroup layout:
import { Switch, SwitchGroup, Label } from '@heroui/react';
function CustomSwitchGroup() {
return (
<SwitchGroup className="gap-8" orientation="horizontal">
<Switch.Root>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label>Option 1</Label>
</Switch.Root>
<Switch.Root>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label>Option 2</Label>
</Switch.Root>
</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:
:hoveror[data-hovered="true"] - Focus:
:focus-visibleor[data-focus-visible="true"](shows focus ring) - Disabled:
:disabledor[aria-disabled="true"](reduced opacity, no pointer events) - Pressed:
:activeor[data-pressed="true"]
API Reference
Switch.Root Props
Inherits from React Aria Switch.
| Prop | Type | Default | Description |
|---|---|---|---|
size | 'sm' | 'md' | 'lg' | 'md' | The size of the switch |
isSelected | boolean | false | Whether the switch is on |
defaultSelected | boolean | false | Whether the switch is on by default (uncontrolled) |
isDisabled | boolean | false | Whether the switch is disabled |
name | string | - | The name of the input element, used when submitting an HTML form |
value | string | - | 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 |
children | React.ReactNode | (values: SwitchRenderProps) => React.ReactNode | - | Switch content or render prop |
SwitchRenderProps
When using the render prop pattern, these values are provided:
| Prop | Type | Description |
|---|---|---|
isSelected | boolean | Whether the switch is currently on |
isHovered | boolean | Whether the switch is hovered |
isPressed | boolean | Whether the switch is currently pressed |
isFocused | boolean | Whether the switch is focused |
isFocusVisible | boolean | Whether the switch is keyboard focused |
isDisabled | boolean | Whether the switch is disabled |
isReadOnly | boolean | Whether the switch is read only |
state | - | State of the switch. |
SwitchGroup Props
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | 'horizontal' | 'vertical' | 'vertical' | The orientation of the switch group |
children | React.ReactNode | - | The switch items to render |
className | string | - | Additional CSS class names |