SwitchUpdated
A toggle switch component for boolean states
Import
import { Switch, SwitchGroup } from '@heroui/react';
Usage
"use client";
import {Label, Switch} from "@heroui/react";
export function Basic() {
return (
<Switch>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm">Enable notifications</Label>
</Switch>
);
}
Anatomy
Import all parts and piece them together.
import {Switch} from '@heroui/react';
export default () => (
<Switch>
<Switch.Control>
<Switch.Thumb>
<Switch.Icon /> {/* Optional */}
</Switch.Thumb>
</Switch.Control>
</Switch>
);
Disabled
"use client";
import {Label, Switch} from "@heroui/react";
export function Disabled() {
return (
<Switch isDisabled>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm">Enable notifications</Label>
</Switch>
);
}
Default Selected
"use client";
import {Label, Switch} from "@heroui/react";
export function DefaultSelected() {
return (
<Switch defaultSelected>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm">Enable notifications</Label>
</Switch>
);
}
Controlled
Switch is off
"use client";
import {Label, Switch} from "@heroui/react";
import React from "react";
export function Controlled() {
const [isSelected, setIsSelected] = React.useState(false);
return (
<div className="flex flex-col gap-4">
<Switch isSelected={isSelected} onChange={setIsSelected}>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm">Enable notifications</Label>
</Switch>
<p className="text-muted text-sm">Switch is {isSelected ? "on" : "off"}</p>
</div>
);
}
Without Label
"use client";
import {Switch} from "@heroui/react";
export function WithoutLabel() {
return (
<Switch aria-label="Enable notifications">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
</Switch>
);
}
Sizes
"use client";
import {Label, Switch} from "@heroui/react";
export function Sizes() {
return (
<div className="flex gap-6">
<Switch size="sm">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-xs">Small</Label>
</Switch>
<Switch size="md">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm">Medium</Label>
</Switch>
<Switch size="lg">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-base">Large</Label>
</Switch>
</div>
);
}
Label Position
"use client";
import {Label, Switch} from "@heroui/react";
export function LabelPosition() {
return (
<div className="flex flex-col gap-4">
<Switch>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm">Label after</Label>
</Switch>
<Switch>
<Label className="text-sm">Label before</Label>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
</Switch>
</div>
);
}
With Icons
"use client";
import {Switch} 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]) => (
<Switch key={key} defaultSelected size="lg">
{({isSelected}) => (
<>
<Switch.Control className={isSelected ? value.selectedControlClass : ""}>
<Switch.Thumb>
<Switch.Icon>
<Icon
className={`${isSelected ? "opacity-100" : "opacity-70"} size-3 text-inherit`}
icon={isSelected ? value.on : value.off}
/>
</Switch.Icon>
</Switch.Thumb>
</Switch.Control>
</>
)}
</Switch>
))}
</div>
);
}
With Description
"use client";
import {Description, Label, Switch} from "@heroui/react";
export function WithDescription() {
return (
<div className="max-w-sm">
<Switch>
<div className="flex gap-3">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<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>
</Switch>
</div>
);
}
Group
"use client";
import {Label, Switch, SwitchGroup} from "@heroui/react";
export function Group() {
return (
<SwitchGroup>
<SwitchGroup.Items>
<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.Items>
</SwitchGroup>
);
}
Group Horizontal
"use client";
import {Label, Switch, SwitchGroup} from "@heroui/react";
export function GroupHorizontal() {
return (
<SwitchGroup className="overflow-x-auto" orientation="horizontal">
<SwitchGroup.Items>
<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.Items>
</SwitchGroup>
);
}
Render Props
"use client";
import {Label, Switch} from "@heroui/react";
export function RenderProps() {
return (
<Switch>
{({isSelected}) => (
<>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm">{isSelected ? "Enabled" : "Disabled"}</Label>
</>
)}
</Switch>
);
}
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>
<SwitchGroup.Items>
<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.Items>
</SwitchGroup>
<Button className="mt-4" size="sm" type="submit" variant="primary">
Submit
</Button>
</form>
);
}
Custom Styles
"use client";
import {Switch} from "@heroui/react";
import {Icon} from "@iconify/react";
export function CustomStyles() {
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 ? "ms-[22px] shadow-lg" : ""}`}
>
<Switch.Icon>
<Icon
className={`size-4 ${isSelected ? "text-cyan-600" : "text-blue-600"}`}
icon={isSelected ? "gravity-ui:check" : "gravity-ui:power"}
/>
</Switch.Icon>
</Switch.Thumb>
</Switch.Control>
</>
)}
</Switch>
);
}
Styling
Passing Tailwind CSS classes
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.Icon>
<Icon
className={`size-4 ${isSelected ? "text-cyan-600" : "text-blue-600"}`}
icon={isSelected ? "gravity-ui:check" : "gravity-ui:power"}
/>
</Switch.Icon>
</Switch.Thumb>
</Switch.Control>
</>
)}
</Switch>
);
}
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
The Switch component uses these CSS classes (View source styles):
Base Classes
.switch
- Base switch container.switch__control
- Switch control track.switch__thumb
- Switch thumb that moves.switch__icon
- Optional icon inside the thumb
Group Classes
.switch-group
- Switch group container.switch-group__items
- Container for switch items.switch-group--horizontal
- Horizontal layout.switch-group--vertical
- Vertical layout
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-hover="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.
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 |