Switch
A switch allows users to turn an individual option on or off.
Features
- Syncs with
disabled
state of fieldset - Syncs with form
reset
events - Can be toggled programmatically
Installation
To use the switch machine in your project, run the following command in your command line:
npm install @zag-js/switch @zag-js/react # or yarn add @zag-js/switch @zag-js/react
npm install @zag-js/switch @zag-js/solid # or yarn add @zag-js/switch @zag-js/solid
npm install @zag-js/switch @zag-js/vue # or yarn add @zag-js/switch @zag-js/vue
npm install @zag-js/switch @zag-js/vue # or yarn add @zag-js/switch @zag-js/vue
This command will install the framework agnostic switch logic and the reactive utilities for your framework of choice.
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-part
attribute to help identify them in the DOM.
Usage
First, import the switch package into your project
import * as zagSwitch from "@zag-js/switch"
The switch package exports two key functions:
machine
— The state machine logic for the switch widget.connect
— The function that translates the machine's state to JSX attributes and event handlers.
Next, import the required hooks and functions for your framework and use the switch machine in your project 🔥
import * as zagSwitch from "@zag-js/switch" import { useMachine, normalizeProps } from "@zag-js/react" function Checkbox() { const [state, send] = useMachine(zagSwitch.machine({ id: "1" })) const api = zagSwitch.connect(state, send, normalizeProps) return ( <label {...api.rootProps}> <input {...api.hiddenInputProps} /> <span {...api.controlProps}> <span {...api.thumbProps} /> </span> <span {...api.labelProps}>{api.isChecked ? "On" : "Off"}</span> </label> ) }
import * as zagSwitch from "@zag-js/switch" import { normalizeProps, useMachine } from "@zag-js/solid" import { createMemo, createUniqueId } from "solid-js" function Checkbox() { const [state, send] = useMachine(zagSwitch.machine({ id: "1" })) const api = createMemo(() => zagSwitch.connect(state, send, normalizeProps)) return ( <label {...api().rootProps}> <input {...api().hiddenInputProps} /> <span {...api().controlProps}> <span {...api().thumbProps} /> </span> <span {...api().labelProps}>{api.isChecked ? "On" : "Off"}</span> </label> ) }
import * as zagSwitch from "@zag-js/switch" import { normalizeProps, useMachine } from "@zag-js/vue" import { defineComponent, h, Fragment, computed } from "vue" export default defineComponent({ name: "Switch", setup() { const [state, send] = useMachine(zagSwitch.machine({ id: "switch" })) const apiRef = computed(() => zagSwitch.connect(state.value, send, normalizeProps), ) return () => { const api = apiRef.value return ( <label {...api.rootProps}> <input {...api.hiddenInputProps} /> <span {...api.controlProps}> <span {...api.thumbProps} /> </span> <span {...api.labelProps}>{api.isChecked ? "On" : "Off"}</span> </label> ) } }, })
<script setup> import * as zagSwitch from "@zag-js/switch" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed } from "vue" const [state, send] = useMachine(zagSwitch.machine({ id: "1" })) const api = computed(() => zagSwitch.connect(state.value, send, normalizeProps), ) </script> <template> <label v-bind="api.rootProps"> <input v-bind="api.inputProps" /> <span v-bind="api.controlProps"> <span v-bind="api.thumbProps" /> </span> <span v-bind="api.labelProps"> <span v-if="api.isChecked">On</span> <span v-else>Off</span> </span> </label> </template>
Disabling the switch
To make a switch disabled, set the context's disabled
property to true
const [state, send] = useMachine( zagSwitch.machine({ disabled: true, }), )
Making it checked by default
To make a switch checked by default, set the context's checked
property to
true
const [state, send] = useMachine( zagSwitch.machine({ checked: true, }), )
Listening for changes
When the switch value changes, the onCheckedChange
callback is invoked.
const [state, send] = useMachine( zagSwitch.machine({ onCheckedChange(details) { // details => { checked: boolean } console.log("switch is:", details.checked ? "On" : "Off") }, }), )
Usage within forms
To use switch within forms, use the exposed inputProps
from the connect
function and ensure you pass name
value to the machine's context. It will
render a hidden input and ensure the value changes get propagated to the form
correctly.
const [state, send] = useMachine( zagSwitch.machine({ name: "feature", }), )
Styling guide
Earlier, we mentioned that each switch part has a data-part
attribute added to
them to select and style them in the DOM.
Focused State
When the switch input is focused, the data-focus
attribute is added to the
root, control and label parts.
[data-part="root"][data-focus] { /* styles for root focus state */ } [data-part="control"][data-focus] { /* styles for control focus state */ } [data-part="label"][data-focus] { /* styles for label focus state */ }
Disabled State
When the switch is disabled, the data-disabled
attribute is added to the root,
control and label parts.
[data-part="root"][data-disabled] { /* styles for root disabled state */ } [data-part="control"][data-disabled] { /* styles for control disabled state */ } [data-part="label"][data-disabled] { /* styles for label disabled state */ }
Invalid State
When the switch is invalid, the data-invalid
attribute is added to the root,
control and label parts.
[data-part="root"][data-invalid] { /* styles for root invalid state */ } [data-part="control"][data-invalid] { /* styles for control invalid state */ } [data-part="label"][data-invalid] { /* styles for label invalid state */ }
Methods and Properties
Machine Context
The switch machine exposes the following context properties:
ids
Partial<{ root: string; input: string; control: string; label: string; thumb: string; }>
The ids of the elements in the switch. Useful for composition.label
string
Specifies the localized strings that identifies the accessibility elements and their statesdisabled
boolean
Whether the switch is disabled.invalid
boolean
If `true`, the switch is marked as invalid.required
boolean
If `true`, the switch input is marked as required,onCheckedChange
(details: CheckedChangeDetails) => void
Function to call when the switch is clicked.checked
boolean
Whether the switch is checked.name
string
The name of the input field in a switch (Useful for form submission).form
string
The id of the form that the switch belongs tovalue
string | number
The value of switch input. Useful for form submission.dir
"ltr" | "rtl"
The document's text/writing direction.id
string
The unique identifier of the machine.getRootNode
() => ShadowRoot | Node | Document
A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
Machine API
The switch api
exposes the following methods:
isChecked
boolean
Whether the checkbox is checkedisDisabled
boolean
Whether the checkbox is disabledisFocused
boolean
Whether the checkbox is focusedsetChecked
(checked: boolean) => void
Function to set the checked state of the switch.toggleChecked
() => void
Function to toggle the checked state of the checkbox
Accessibility
Adheres to the Switch WAI-ARIA design pattern
Keyboard Interactions
- SpaceEnterToggle the switch
Edit this page on GitHub