Toggle
A button that stays on or off, with a fill that settles in when pressed, a clear hover-to-on step and icons that can fill with the state.
Used in Pro blocks: Artifact and Code Block
pnpm dlx shadcn@latest add https://hextaui.com/r/toggle.jsonAdds the component, the HextaUI theme tokens and any HextaUI components it depends on.
Add the theme tokens to your global CSS, if you haven’t yet.
Install the dependencies.
pnpm add @base-ui/react class-variance-authority cnCopy and paste the following code into your project.
components/ui/toggle.tsx Update the import paths to match your project setup.
Hovering an off toggle shows a light tint; turning it on settles a stronger fill into place and brings the label to full contrast, so on never looks like hover.
variant="outline" adds a border and a surface, for toggles that sit on their own.
Icons and labels sit side by side. Toggles with visible text don't need an aria-label.
Add group-data-pressed/toggle:fill-current to an outline icon and it fills in as the toggle turns on.
sm, default and lg. Icon-only toggles stay square.
shape="pill" rounds the ends fully, for toggles inside rounded surfaces like a chat composer.
A disabled toggle keeps showing whether it's on, but can't be pressed or focused.
Pass pressed and onPressedChange. The label stays the same while the icon follows the state.
Icons lead the label from the right.
| Key | Action |
|---|---|
| SpaceEnter | Turns the focused toggle on or off. |
| Tab | Moves focus to the next toggle. |
- The toggle is a
buttonwitharia-pressed, so screen readers announce whether it's on. - Give icon-only toggles an
aria-labelthat names the action ("Mute microphone") and keep it the same in both states. Changing it as well as the pressed state reads as a double negative. - On touch screens the hit area grows to at least 44px, except inside a toggle group where neighbours would overlap.
- With reduced motion, nothing scales; the fill only fades.
| Prop | Type | Default |
|---|---|---|
variant | "default" | "outline" | "default" |
size | "default" | "sm" | "lg" | "default" |
shape | "default" | "pill" | "default" |
pressed | boolean | – |
defaultPressed | boolean | false |
onPressedChange | (pressed, details) => void | – |
disabled | boolean | false |
valueIdentifies the toggle inside a toggle group. | string | – |
className | string | (state) => string | – |
nativeButton | boolean | true |
render | ReactElement | (props, state) => ReactElement | <button> |
| Attribute | Description |
|---|---|
data-slot="toggle" | The button, with data-variant and data-size. |
data-pressed | Present when the toggle is on. |
data-disabled | Present when the toggle is disabled. |
- ButtonButtons in every variant and size, with a built-in loading, success and error flow that skips the spinner for fast requests.
- Button groupButtons joined into one control, with shared seams, separators, text addons, nesting and vertical stacks.
- Toggle groupA row of toggles for one choice or many, with a fill that slides to the picked item, joined or spaced items and arrow-key focus.
Used in Pro blocks
Blocks that build on Toggle.
- ArtifactThe panel beside an AI chat that shows what the model made. Handles content still streaming, a sandboxed preview and versions you can restore.
- Code BlockCode blocks for AI answers. Highlighting keeps up with streaming, diffs can be accepted or rejected, and commands run in a terminal.
Every block comes with HextaUI Pro, and Prompt Input is free.