Switch

Switches are a pleasant interface for toggling a value between two states, and offer the same semantics and keyboard navigation as native checkbox elements.

To get started, install Headless UI via npm:

npm install @headlessui/react

Switches are built using the Switch component. You can toggle your switch by clicking directly on the component, or by pressing the spacebar while it's focused.

Toggling the switch calls the onChange function with a negated version of the checked value.

import { Switch } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
    <Switch
      checked={enabled}
      onChange={setEnabled}
      className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 transition data-[checked]:bg-blue-600"
    >
      <span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" />
    </Switch>
  )
}

Headless UI keeps track of a lot of state about each component, like whether or not a switch is checked, whether a popover is open or closed, or which item in a menu is currently focused via the keyboard.

But because the components are headless and completely unstyled out of the box, you can't see this information in your UI until you provide the styles you want for each state yourself.

The easiest way to style the different states of a Headless UI component is using the data-* attributes that each component exposes.

For example, the Switch component exposes a data-checked attribute, which tells you if the switch is currently checked, and a data-disabled attribute, which tells you if the switch is currently disabled.

<!-- Rendered `Switch` -->
<button data-checked data-disabled>
  <!-- ... -->
</button>

Use the CSS attribute selector to conditionally apply styles based on the presence of these data attributes. If you're using Tailwind CSS, the data attribute modifier makes this easy:

import { Switch } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
    <Switch
      checked={enabled}
      onChange={setEnabled}
className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 data-[checked]:bg-blue-600 data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50"
>
<span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" />
</Switch> ) }

See the component API for a list of all the available data attributes.

Each component also exposes information about its current state via render props that you can use to conditionally apply different styles or render different content.

For example, the Switch component exposes a checked state, which tells you if the switch is currently checked, and a disabled state, which tells you if the switch is currently disabled.

import { Switch } from '@headlessui/react'
import clsx from 'clsx'
import { Fragment, useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
    <Switch checked={enabled} onChange={setEnabled} as={Fragment}>
{({ checked, disabled }) => (
<button className={clsx( 'group inline-flex h-6 w-11 items-center rounded-full',
checked ? 'bg-blue-600' : 'bg-gray-200',
disabled && 'cursor-not-allowed opacity-50'
)}
>
<span className="sr-only">Enable notifications</span> <span
className={clsx('size-4 rounded-full bg-white transition', checked ? 'translate-x-6' : 'translate-x-1')}
/>
</button>
)}
</Switch> ) }

See the component API for a list of all the available render props.

Wrap a Label and Switch with the Field component to automatically associate them using a generated ID:

import { Field, Label, Switch } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
<Field>
<Label>Enable notifications</Label>
<Switch checked={enabled} onChange={setEnabled} className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 transition data-[checked]:bg-blue-600" > <span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" /> </Switch>
</Field>
) }

By default, clicking the Label will toggle the Switch, just like labels do for native HTML checkboxes. If you'd like to make the Label non-clickable, you can add a passive prop to the Label component:

<Label passive>Enable beta features</Label>

Use the Description component within a Field to automatically associate it with a Switch using the aria-describedby attribute:

import { Description, Field, Label, Switch } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
<Field>
<Label>Enable notifications</Label>
<Description>Get notified about important changes in your projects.</Description>
<Switch checked={enabled} onChange={setEnabled} className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 transition data-[checked]:bg-blue-600" > <span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" /> </Switch>
</Field>
) }

Add the disabled prop to the Field component to disable a Switch and its associated Label and Description:

import { Description, Field, Label, Switch } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
<Field disabled>
<Label className="data-[disabled]:opacity-50">Enable notifications</Label> <Description className="data-[disabled]:opacity-50"> Get notified about important changes in your projects. </Description> <Switch checked={enabled} onChange={setEnabled} className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 transition data-[checked]:bg-blue-600 data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50" > <span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" /> </Switch> </Field> ) }

You can also disable a switch outside of a Field by adding the disabled prop directly to the Switch itself.

If you add the name prop to your Switch, a hidden input element will be rendered and kept in sync with the switch state.

import { Switch } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
    <form action="/accounts" method="post">
      <Switch
        checked={enabled}
        onChange={setEnabled}
name="terms-of-service"
className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 transition data-[checked]:bg-blue-600" >
<span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" /> </Switch> <button>Submit</button> </form> ) }

This lets you use a switch inside a native HTML <form> and make traditional form submissions as if your switch was a native HTML form control.

By default, the value will be on when the switch is checked, and not present when the switch is unchecked.

<!-- Rendered hidden input -->
<input type="hidden" name="terms-of-service" value="on" />

You can customize the value if needed by using the value prop:

import { Switch } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
    <form action="/accounts" method="post">
      <Switch
        checked={enabled}
        onChange={setEnabled}
        name="terms-of-service"
value="accept"
className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 transition data-[checked]:bg-blue-600" >
<span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" /> </Switch> <button>Submit</button> </form> ) }

The hidden input will then use your custom value when the switch is checked:

<!-- Rendered hidden input -->
<input type="hidden" name="terms-of-service" value="accept" />

Basic values like strings will be rendered as a single hidden input containing that value, but complex values like objects will be encoded into multiple inputs using a square bracket notation for the names.

If you omit the checked prop, Headless UI will track its state internally for you, allowing you to use it as an uncontrolled component.

When uncontrolled, you can check the Switch by default using the defaultChecked prop.

import { Switch } from '@headlessui/react'

function Example() {
  return (
    <form action="/accounts" method="post">
      <Switch
defaultChecked={true}
name="terms-of-service" className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 transition data-[checked]:bg-blue-600" >
<span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" /> </Switch> <button>Submit</button> </form> ) }

This can simplify your code when using the switch with HTML forms or with form APIs that collect their state using FormData instead of tracking it using React state.

Any onChange prop you provide will still be called when the component's value changes in case you need to run any side effects, but you won't need to use it to track the component's state yourself.

Because switches are typically always rendered to the DOM (rather than being mounted/unmounted like other components), simple CSS transitions are often enough to animate your switch:

import { Switch } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
    <Switch
      checked={enabled}
      onChange={setEnabled}
className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 transition data-[checked]:bg-blue-600"
>
<span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" />
</Switch> ) }

Because they're renderless, Headless UI components also compose well with other animation libraries in the React ecosystem like Framer Motion and React Spring.

The Switch component renders a button by default.

Use the as prop to render the component as a different element or as your own custom component, making sure your custom components forward refs so that Headless UI can wire things up correctly.

import { Switch } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
    <Switch
as="div"
checked={enabled} onChange={setEnabled} className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 transition data-[checked]:bg-blue-600" >
<span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" /> </Switch> ) }

CommandDescription

Spacewhen a Switch is focused

Toggles the Switch

Enterwhen in a form

Submits the form

The main switch component.

PropDefaultDescription
asbutton
String | Component

The element or component the switch should render as.

checked
Boolean

Whether or not the switch is checked.

defaultChecked
T

The default checked value when using as an uncontrolled component.

onChange
(value: Boolean) => void

The function to call when the switch is toggled.

name
String

The name used when using the switch inside a form.

form
String

The id of the form that the switch belongs to.

If name is provided but form is not, the switch will add its state to the nearest ancestor form element.

value
String

The value used when using this component inside a form, if it is checked.

Data AttributeRender PropDescription
data-checkedchecked

Boolean

Whether or not the switch is checked.

data-disableddisabled

Boolean

Whether or not the switch is disabled.

data-focusfocus

Boolean

Whether or not the switch is focused.

data-hoverhover

Boolean

Whether or not the switch is hovered.

data-activeactive

Boolean

Whether or not the switch is in an active or pressed state.

data-autofocusautofocus

Boolean

Whether or not the autoFocus prop was set to true.

data-changingchanging

Boolean

Whether or not the checked state is currently changing.

When the checked state changes, changing will be true for two animation frames, allowing you to fine-tune transitions.

Groups a Label, Description, and form control together.

PropDefaultDescription
asdiv
String | Component

The element or component the field should render as.

disabledfalse
Boolean

Whether or not the field is disabled.

Data AttributeRender PropDescription
data-disableddisabled

Boolean

Whether or not the field is disabled.

The Label component labels a form control.

PropDefaultDescription
aslabel
String | Component

The element or component the label should render as.

passivefalse
Boolean

When true, clicking the label won't focus the associated form control.

Data AttributeRender PropDescription
data-disableddisabled

Boolean

Whether or not the parent Field is disabled.

The Description component describes a form control.

PropDefaultDescription
asp
String | Component

The element or component the description should render as.

Data AttributeRender PropDescription
data-disableddisabled

Boolean

Whether or not the parent Field is disabled.

If you're interested in predesigned component examples using Headless UI and Tailwind CSS, check out Tailwind UI — a collection of beautifully designed and expertly crafted components built by us.

It's a great way to support our work on open-source projects like this and makes it possible for us to improve them and keep them well-maintained.