Switch
A control element that allows for a binary selection.
Anatomy
To set up the switch correctly, you'll need to understand its anatomy and how we name its parts.
Each part includes a
data-partattribute to help identify them in the DOM.
Design impact on the asChild property
The Switch.Root element of the switch is a label element. This is because the switch is a form control and should be
associated with a label to provide context and meaning to the user. Otherwise, the HTML and accessibility structure will
be invalid.
If you need to use the
asChildproperty, make sure that thelabelelement is the direct child of theSwitch.Rootcomponent.
Examples
Learn how to use the Switch component in your project. Let's take a look at the most basic example:
import { Switch } from '@ark-ui/react/switch'
import styles from 'styles/switch.module.css'
export const Basic = () => (
<Switch.Root className={styles.Root}>
<Switch.Control className={styles.Control}>
<Switch.Thumb className={styles.Thumb} />
</Switch.Control>
<Switch.Label className={styles.Label}>Label</Switch.Label>
<Switch.HiddenInput />
</Switch.Root>
)
import { Switch } from '@ark-ui/solid/switch'
import styles from 'styles/switch.module.css'
export const Basic = () => (
<Switch.Root class={styles.Root}>
<Switch.Control class={styles.Control}>
<Switch.Thumb class={styles.Thumb} />
</Switch.Control>
<Switch.Label class={styles.Label}>Label</Switch.Label>
<Switch.HiddenInput />
</Switch.Root>
)
<script setup lang="ts">
import { Switch } from '@ark-ui/vue/switch'
import styles from 'styles/switch.module.css'
</script>
<template>
<Switch.Root :class="styles.Root">
<Switch.Control :class="styles.Control">
<Switch.Thumb :class="styles.Thumb" />
</Switch.Control>
<Switch.Label :class="styles.Label">Label</Switch.Label>
<Switch.HiddenInput />
</Switch.Root>
</template>
<script lang="ts">
import { Switch } from '@ark-ui/svelte/switch'
import styles from 'styles/switch.module.css'
</script>
<Switch.Root class={styles.Root}>
<Switch.Control class={styles.Control}>
<Switch.Thumb class={styles.Thumb} />
</Switch.Control>
<Switch.Label class={styles.Label}>Label</Switch.Label>
<Switch.HiddenInput />
</Switch.Root>
Controlled Switch
For a controlled Switch component, the state of the toggle is managed using the checked prop, and updates when the
onCheckedChange event handler is called:
import { Switch } from '@ark-ui/react/switch'
import { useState } from 'react'
import styles from 'styles/switch.module.css'
export const Controlled = () => {
const [checked, setChecked] = useState(false)
return (
<Switch.Root className={styles.Root} checked={checked} onCheckedChange={(e) => setChecked(e.checked)}>
<Switch.Control className={styles.Control}>
<Switch.Thumb className={styles.Thumb} />
</Switch.Control>
<Switch.Label className={styles.Label}>Label</Switch.Label>
<Switch.HiddenInput />
</Switch.Root>
)
}
import { Switch } from '@ark-ui/solid/switch'
import { createSignal } from 'solid-js'
import styles from 'styles/switch.module.css'
export const Controlled = () => {
const [checked, setChecked] = createSignal(false)
return (
<Switch.Root class={styles.Root} checked={checked()} onCheckedChange={(e) => setChecked(e.checked)}>
<Switch.Control class={styles.Control}>
<Switch.Thumb class={styles.Thumb} />
</Switch.Control>
<Switch.Label class={styles.Label}>Label</Switch.Label>
<Switch.HiddenInput />
</Switch.Root>
)
}
<script setup lang="ts">
import { Switch } from '@ark-ui/vue/switch'
import { ref } from 'vue'
import styles from 'styles/switch.module.css'
const checked = ref(false)
</script>
<template>
<Switch.Root :class="styles.Root" v-model:checked="checked">
<Switch.Control :class="styles.Control">
<Switch.Thumb :class="styles.Thumb" />
</Switch.Control>
<Switch.Label :class="styles.Label">Label</Switch.Label>
<Switch.HiddenInput />
</Switch.Root>
</template>
<script lang="ts">
import { Switch } from '@ark-ui/svelte/switch'
import styles from 'styles/switch.module.css'
let checked = $state(false)
</script>
<Switch.Root class={styles.Root} bind:checked>
<Switch.Control class={styles.Control}>
<Switch.Thumb class={styles.Thumb} />
</Switch.Control>
<Switch.Label class={styles.Label}>Label</Switch.Label>
<Switch.HiddenInput />
</Switch.Root>
Using the Context
The Switch component allows access to its internal state through the Context component. This enables you to
dynamically adjust and customize aspects of the component based on its current state:
import { Switch } from '@ark-ui/react/switch'
import styles from 'styles/switch.module.css'
export const Context = () => (
<Switch.Root className={styles.Root}>
<Switch.Control className={styles.Control}>
<Switch.Thumb className={styles.Thumb} />
</Switch.Control>
<Switch.Context>
{(context) => (
<Switch.Label className={styles.Label}>Feature is {context.checked ? 'enabled' : 'disabled'}</Switch.Label>
)}
</Switch.Context>
<Switch.HiddenInput />
</Switch.Root>
)
import { Switch } from '@ark-ui/solid/switch'
import styles from 'styles/switch.module.css'
export const Context = () => (
<Switch.Root class={styles.Root}>
<Switch.Control class={styles.Control}>
<Switch.Thumb class={styles.Thumb} />
</Switch.Control>
<Switch.Context>
{(context) => (
<Switch.Label class={styles.Label}>Feature is {context().checked ? 'enabled' : 'disabled'}</Switch.Label>
)}
</Switch.Context>
<Switch.HiddenInput />
</Switch.Root>
)
<script setup lang="ts">
import { Switch } from '@ark-ui/vue/switch'
import styles from 'styles/switch.module.css'
</script>
<template>
<Switch.Root :class="styles.Root">
<Switch.Control :class="styles.Control">
<Switch.Thumb :class="styles.Thumb" />
</Switch.Control>
<Switch.Context v-slot="api">
<Switch.Label :class="styles.Label">Feature is {{ api.checked ? 'enabled' : 'disabled' }}</Switch.Label>
</Switch.Context>
<Switch.HiddenInput />
</Switch.Root>
</template>
<script lang="ts">
import { Switch } from '@ark-ui/svelte/switch'
import styles from 'styles/switch.module.css'
</script>
<Switch.Root class={styles.Root}>
<Switch.Control class={styles.Control}>
<Switch.Thumb class={styles.Thumb} />
</Switch.Control>
<Switch.Context>
{#snippet render(context)}
<Switch.Label class={styles.Label}>Feature is {context().checked ? 'enabled' : 'disabled'}</Switch.Label>
{/snippet}
</Switch.Context>
<Switch.HiddenInput />
</Switch.Root>
Using the Field Component
The Field component helps manage form-related state and accessibility attributes of a switch. It includes handling
ARIA labels, helper text, and error text to ensure proper accessibility.
import { Field } from '@ark-ui/react/field'
import { Switch } from '@ark-ui/react/switch'
import field from 'styles/field.module.css'
import styles from 'styles/switch.module.css'
export const WithField = () => (
<Field.Root className={field.Root}>
<Switch.Root className={styles.Root}>
<Switch.Control className={styles.Control}>
<Switch.Thumb className={styles.Thumb} />
</Switch.Control>
<Switch.Label className={styles.Label}>Label</Switch.Label>
<Switch.HiddenInput />
</Switch.Root>
<Field.HelperText className={field.HelperText}>Additional Info</Field.HelperText>
<Field.ErrorText className={field.ErrorText}>Error Info</Field.ErrorText>
</Field.Root>
)
import { Field } from '@ark-ui/solid/field'
import { Switch } from '@ark-ui/solid/switch'
import field from 'styles/field.module.css'
import styles from 'styles/switch.module.css'
export const WithField = () => (
<Field.Root class={field.Root}>
<Switch.Root class={styles.Root}>
<Switch.Control class={styles.Control}>
<Switch.Thumb class={styles.Thumb} />
</Switch.Control>
<Switch.Label class={styles.Label}>Label</Switch.Label>
<Switch.HiddenInput />
</Switch.Root>
<Field.HelperText class={field.HelperText}>Additional Info</Field.HelperText>
<Field.ErrorText class={field.ErrorText}>Error Info</Field.ErrorText>
</Field.Root>
)
<script setup lang="ts">
import { Field } from '@ark-ui/vue/field'
import { Switch } from '@ark-ui/vue/switch'
import field from 'styles/field.module.css'
import styles from 'styles/switch.module.css'
</script>
<template>
<Field.Root :class="field.Root">
<Switch.Root :class="styles.Root">
<Switch.Control :class="styles.Control">
<Switch.Thumb :class="styles.Thumb" />
</Switch.Control>
<Switch.Label :class="styles.Label">Label</Switch.Label>
<Switch.HiddenInput />
</Switch.Root>
<Field.HelperText :class="field.HelperText">Additional Info</Field.HelperText>
<Field.ErrorText :class="field.ErrorText">Error Info</Field.ErrorText>
</Field.Root>
</template>
<script lang="ts">
import { Field } from '@ark-ui/svelte/field'
import { Switch } from '@ark-ui/svelte/switch'
import field from 'styles/field.module.css'
import styles from 'styles/switch.module.css'
</script>
<Field.Root class={field.Root}>
<Switch.Root class={styles.Root}>
<Switch.Control class={styles.Control}>
<Switch.Thumb class={styles.Thumb} />
</Switch.Control>
<Switch.Label class={styles.Label}>Label</Switch.Label>
<Switch.HiddenInput />
</Switch.Root>
<Field.HelperText class={field.HelperText}>Additional Info</Field.HelperText>
<Field.ErrorText class={field.ErrorText}>Error Info</Field.ErrorText>
</Field.Root>
Using the Root Provider
The RootProvider component provides a context for the switch. It accepts the value of the useSwitch hook. You can
leverage it to access the component state and methods from outside the switch.
import { Switch, useSwitch } from '@ark-ui/react/switch'
import button from 'styles/button.module.css'
import styles from 'styles/switch.module.css'
export const RootProvider = () => {
const _switch = useSwitch()
return (
<div className="stack">
<button className={button.Root} onClick={() => _switch.toggleChecked()}>
Toggle
</button>
<Switch.RootProvider className={styles.Root} value={_switch}>
<Switch.Control className={styles.Control}>
<Switch.Thumb className={styles.Thumb} />
</Switch.Control>
<Switch.Label className={styles.Label}>Label</Switch.Label>
<Switch.HiddenInput />
</Switch.RootProvider>
</div>
)
}
import { Switch, useSwitch } from '@ark-ui/solid/switch'
import button from 'styles/button.module.css'
import styles from 'styles/switch.module.css'
export const RootProvider = () => {
const _switch = useSwitch()
return (
<div class="stack">
<button class={button.Root} onClick={() => _switch().toggleChecked()}>
Toggle
</button>
<Switch.RootProvider class={styles.Root} value={_switch}>
<Switch.Control class={styles.Control}>
<Switch.Thumb class={styles.Thumb} />
</Switch.Control>
<Switch.Label class={styles.Label}>Label</Switch.Label>
<Switch.HiddenInput />
</Switch.RootProvider>
</div>
)
}
<script setup lang="ts">
import { Switch, useSwitch } from '@ark-ui/vue/switch'
import button from 'styles/button.module.css'
import styles from 'styles/switch.module.css'
const _switch = useSwitch()
</script>
<template>
<div class="stack">
<button :class="button.Root" @click="_switch.toggleChecked()">Toggle</button>
<Switch.RootProvider :class="styles.Root" :value="_switch">
<Switch.Control :class="styles.Control">
<Switch.Thumb :class="styles.Thumb" />
</Switch.Control>
<Switch.Label :class="styles.Label">Label</Switch.Label>
<Switch.HiddenInput />
</Switch.RootProvider>
</div>
</template>
<script lang="ts">
import { Switch, useSwitch } from '@ark-ui/svelte/switch'
import button from 'styles/button.module.css'
import styles from 'styles/switch.module.css'
const id = $props.id()
const _switch = useSwitch({ id })
</script>
<div class="stack">
<button class={button.Root} onclick={() => _switch().toggleChecked()}>Toggle</button>
<Switch.RootProvider class={styles.Root} value={_switch}>
<Switch.Control class={styles.Control}>
<Switch.Thumb class={styles.Thumb} />
</Switch.Control>
<Switch.Label class={styles.Label}>Label</Switch.Label>
<Switch.HiddenInput />
</Switch.RootProvider>
</div>
If you're using the
RootProvidercomponent, you don't need to use theRootcomponent.
API Reference
Props
Root
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
checked | booleanThe controlled checked state of the switch | |
disabled | booleanWhether the switch is disabled. | |
ids | Partial<{ root: string; hiddenInput: string; control: string; label: string; thumb: string }>The ids of the elements in the switch. Useful for composition. | |
invalid | booleanIf `true`, the switch is marked as invalid. | |
label | stringSpecifies the localized strings that identifies the accessibility elements and their states | |
name | stringThe name of the input field in a switch (Useful for form submission). | |
onCheckedChange | (details: CheckedChangeDetails) => voidFunction to call when the switch is clicked. | |
readOnly | booleanWhether the switch is read-only | |
required | booleanIf `true`, the switch input is marked as required, | |
value | 'on' | string | numberThe value of switch input. Useful for form submission. |
| Data Attribute | Value |
|---|---|
[data-active] | Present when active or pressed |
[data-focus] | Present when focused |
[data-focus-visible] | Present when focused with keyboard |
[data-readonly] | Present when read-only |
[data-hover] | Present when hovered |
[data-disabled] | Present when disabled |
[data-state] | "checked" | "unchecked" |
[data-invalid] | Present when invalid |
[data-required] | Present when required |
Control
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-active] | Present when active or pressed |
[data-focus] | Present when focused |
[data-focus-visible] | Present when focused with keyboard |
[data-readonly] | Present when read-only |
[data-hover] | Present when hovered |
[data-disabled] | Present when disabled |
[data-state] | "checked" | "unchecked" |
[data-invalid] | Present when invalid |
[data-required] | Present when required |
HiddenInput
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Label
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-active] | Present when active or pressed |
[data-focus] | Present when focused |
[data-focus-visible] | Present when focused with keyboard |
[data-readonly] | Present when read-only |
[data-hover] | Present when hovered |
[data-disabled] | Present when disabled |
[data-state] | "checked" | "unchecked" |
[data-invalid] | Present when invalid |
[data-required] | Present when required |
RootProvider
| Prop | Default | Type |
|---|---|---|
value | UseSwitchReturn | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Thumb
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-active] | Present when active or pressed |
[data-focus] | Present when focused |
[data-focus-visible] | Present when focused with keyboard |
[data-readonly] | Present when read-only |
[data-hover] | Present when hovered |
[data-disabled] | Present when disabled |
[data-state] | "checked" | "unchecked" |
[data-invalid] | Present when invalid |
[data-required] | Present when required |
Context
These are the properties available when using Switch.Context, useSwitchContext hook or useSwitch hook.
API
| Property | Type |
|---|---|
checked | booleanWhether the switch is checked |
disabled | booleanWhether the switch is disabled |
focused | booleanWhether the switch is focused |
setChecked | (checked: boolean) => voidSets the checked state of the switch. |
toggleChecked | VoidFunctionToggles the checked state of the switch. |
Accessibility
Complies with the Switch WAI-ARIA design pattern.
Keyboard Support
| Key | Description |
|---|---|
SpaceEnter | Toggle the switch |