Switch (Toggle)
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.
Please note that this library only supports Vue 3.
npm install @headlessui/vue
Switches are built using the Switch
component, which takes in a ref via the v-model
prop. You can toggle your Switch by clicking directly on the component, or by pressing the spacebar while its focused.
Toggling the switch updates your ref to its negated value.
<template> <Switch v-model="enabled" :class="enabled ? 'bg-teal-900' : 'bg-teal-700'" class="relative inline-flex h-6 w-11 items-center rounded-full" > <span class="sr-only">Enable notifications</span> <span :class="enabled ? 'translate-x-6' : 'translate-x-1'" class="inline-block h-4 w-4 transform rounded-full bg-white" /> </Switch> </template> <script setup> import { ref } from 'vue' import { Switch } from '@headlessui/vue' const enabled = ref(false) </script>
By default, a Switch renders a button
as well as whatever children you pass into it. This can make it harder to implement certain UIs, since the children will be nested within the button.
In these situations, you can use the SwitchLabel
component for more flexibility.
This example demonstrates how to use the SwitchGroup
, Switch
and SwitchLabel
components to render a label as a sibling to the button. Note that SwitchLabel
works alongside a Switch
component, and they both must be rendered within a parent SwitchGroup
component.
<template>
<SwitchGroup><div class="flex items-center"><SwitchLabel class="mr-4">Enable notifications</SwitchLabel><Switch v-model="enabled" :class='enabled ? "bg-blue-600" : "bg-gray-200"' class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" > <span :class='enabled ? "translate-x-6" : "translate-x-1"' class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform" /> </Switch> </div></SwitchGroup></template> <script setup> import { ref } from 'vue' import { Switch, SwitchGroup, SwitchLabel } from '@headlessui/vue' const enabled = ref(false) </script>
By default, clicking a SwitchLabel
will toggle the Switch, just like labels in native HTML checkboxes do. If you'd like to make the label non-clickable (which you might if it doesn't make sense for your design), you can add a passive
prop to the SwitchLabel
component:
<template> <SwitchGroup>
<SwitchLabel passive>Enable notifications</SwitchLabel><Switch v-model="enabled"> <!-- ... --> </Switch> </SwitchGroup> </template> <script setup> import { ref } from 'vue' import { Switch, SwitchGroup, SwitchLabel } from '@headlessui/vue' const enabled = ref(false) </script>
If you add the name
prop to your switch, a hidden input
element will be rendered and kept in sync with the switch state.
<template> <form action="/notification-settings" method="post">
<Switch v-model="enabled" name="notifications"><!-- ... --> </Switch> </form> </template> <script setup> import { ref } from 'vue' import { Switch } from '@headlessui/vue' const enabled = ref(true) </script>
This lets you use a radio group inside a native HTML <form>
and make traditional form submissions as if your radio group 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.
<input type="hidden" name="notifications" value="on" />
You can customize the value if needed by using the value
prop:
<template> <form action="/accounts" method="post">
<Switch v-model="enabled" name="terms" value="accept"><!-- ... --> </Switch> </form> </template> <script setup> import { ref } from 'vue' import { Switch } from '@headlessui/vue' const enabled = ref(true) </script>
The hidden input will then use your custom value when the switch is checked:
<input type="hidden" name="terms" value="accept" />
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:
<template> <Switch v-model="enabled"> <!-- Transition the Switch's knob on state change --> <span
:class="enabled ? 'translate-x-9' : 'translate-x-0'"class="transform transition duration-200 ease-in-out" /> <!-- ... --> </Switch> </template> <script setup> import { ref } from 'vue' import { Switch } from '@headlessui/vue' const enabled = ref(false) </script>
By default, the children of a Switch
will be used as the label for screen readers. If you're using SwitchLabel
, the content of your Switch
component will be ignored by assistive technologies.
Clicking a Switch
or a SwitchLabel
toggles the Switch on and off.
Command | Description |
Space when a | Toggles the Switch |
Prop | Default | Description |
as | button | String | Component The element or component the |
v-model | — | Boolean Whether or not the switch is checked. |
name | — | String The name used when using this component inside a form. |
value | — | String The value used when using this component inside a form, if it is checked. |
Slot Prop | Description |
checked |
Whether or not the switch is checked. |
Prop | Default | Description |
as | label | String | Component The element or component the |
passive | false | Boolean When true, clicking the label won't toggle the |
Prop | Default | Description |
as | p | String | Component The element or component the |
Prop | Default | Description |
as | template | String | Component The element or component the |
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.