Checkbox
A checkbox allows users to make a binary choice, i.e. a choice between one of two possible mutually exclusive options.
Features
- Tri-state checkbox (
indeterminatestate) - Syncs with
disabledstate of fieldset - Syncs with form
resetevents - Can be toggled programmatically
Installation
Install the checkbox package:
npm install @zag-js/checkbox @zag-js/react # or yarn add @zag-js/checkbox @zag-js/react
npm install @zag-js/checkbox @zag-js/solid # or yarn add @zag-js/checkbox @zag-js/solid
npm install @zag-js/checkbox @zag-js/vue # or yarn add @zag-js/checkbox @zag-js/vue
npm install @zag-js/checkbox @zag-js/svelte # or yarn add @zag-js/checkbox @zag-js/svelte
Anatomy
Check the checkbox anatomy and part names.
Each part includes a
data-partattribute to help identify them in the DOM.
Usage
Import the checkbox package:
import * as checkbox from "@zag-js/checkbox"
The checkbox package exports two key functions:
machine- State machine logic.connect- Maps machine state to JSX props and event handlers.
Then use the framework integration helpers:
import * as checkbox from "@zag-js/checkbox" import { useMachine, normalizeProps } from "@zag-js/react" import { useId } from "react" function Checkbox() { const service = useMachine(checkbox.machine, { id: useId() }) const api = checkbox.connect(service, normalizeProps) return ( <label {...api.getRootProps()}> <span {...api.getLabelProps()}> Input is {api.checked ? "checked" : "unchecked"} </span> <div {...api.getControlProps()} /> <input {...api.getHiddenInputProps()} /> </label> ) }
import * as checkbox from "@zag-js/checkbox" import { normalizeProps, useMachine } from "@zag-js/solid" import { createMemo, createUniqueId } from "solid-js" function Checkbox() { const service = useMachine(checkbox.machine, { id: createUniqueId() }) const api = createMemo(() => checkbox.connect(service, normalizeProps)) return ( <label {...api().getRootProps()}> <span {...api().getLabelProps()}> Input is {api().checked ? "checked" : "unchecked"} </span> <div {...api().getControlProps()} /> <input {...api().getHiddenInputProps()} /> </label> ) }
<script setup> import * as checkbox from "@zag-js/checkbox" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed } from "vue" const service = useMachine(checkbox.machine, { id: "1" }) const api = computed(() => checkbox.connect(service, normalizeProps)) </script> <template> <label v-bind="api.getRootProps()"> <span v-bind="api.getLabelProps()"> Input is <span v-if="api.checked"> checked</span> <span v-else> unchecked</span> </span> <div v-bind="api.getControlProps()" /> <input v-bind="api.getHiddenInputProps()" /> </label> </template>
<script lang="ts"> import * as checkbox from "@zag-js/checkbox" import { normalizeProps, useMachine } from "@zag-js/svelte" const id = $props.id() const service = useMachine(checkbox.machine, ({ id })) const api = $derived(checkbox.connect(service, normalizeProps)) </script> <label {...api.getRootProps()}> <span {...api.getLabelProps()}> Input is {api.checked ? "checked" : "unchecked"} </span> <div {...api.getControlProps()}></div> <input {...api.getHiddenInputProps()} /> </label>
Setting the initial checked state
Set defaultChecked to true to start checked.
const service = useMachine(checkbox.machine, { defaultChecked: true, })
Indeterminate checkboxes
Set defaultChecked or checked to "indeterminate" for a tri-state checkbox.
const service = useMachine(checkbox.machine, { defaultChecked: "indeterminate", })
Controlled checkbox
Use checked and onCheckedChange to control state externally.
import { useState } from "react" export function ControlledCheckbox() { const [checked, setChecked] = useState(false) const service = useMachine(checkbox.machine, { checked, onCheckedChange(details) { setChecked(details.checked) }, }) return ( // ... ) }
import { createSignal } from "solid-js" export function ControlledCheckbox() { const [checked, setChecked] = createSignal(false) const service = useMachine(checkbox.machine, () => ({ checked: checked(), onCheckedChange(details) { setChecked(details.checked) }, })) return ( // ... ) }
<script setup lang="ts"> import { ref, computed } from "vue" const checkedRef = ref(false) const service = useMachine( checkbox.machine, computed(() => ({ checked: checkedRef.value, onCheckedChange(details) { checkedRef.value = details.checked }, })), ) </script>
<script lang="ts"> let checked = $state(false) const service = useMachine(checkbox.machine, () => ({ checked, onCheckedChange(details) { checked = details.checked }, })) </script>
Disabling the checkbox
Set disabled to true to prevent interaction.
const service = useMachine(checkbox.machine, { disabled: true, })
Listening for changes
When the checked state changes, onCheckedChange is invoked.
const service = useMachine(checkbox.machine, { onCheckedChange(details) { // details => { checked: boolean | "indeterminate" } console.log("checkbox is:", details.checked) }, })
Read-only checkbox
Set readOnly to keep the checkbox focusable but prevent toggling.
const service = useMachine(checkbox.machine, { readOnly: true, })
Usage within forms
To use checkbox within forms, set name and render api.getHiddenInputProps().
const service = useMachine(checkbox.machine, { name: "fruits", })
Next, render the hidden input and ensure the value changes get propagated to the form correctly.
<input {...api.getHiddenInputProps()} />
Customizing submitted value
Set value to customize the submitted form value when checked.
const service = useMachine(checkbox.machine, { name: "newsletter", value: "subscribed", })
Associating with an external form
If the input belongs to a different form element, set form.
const service = useMachine(checkbox.machine, { name: "newsletter", form: "settings-form", })
Styling guide
Each part includes a data-part attribute you can target in CSS.
Checked state
When the checkbox input is checked, the data-state attribute is added to the
root, control and label parts.
[data-part="root"][data-state="checked|unchecked|indeterminate"] { /* styles for when checkbox is checked */ } [data-part="control"][data-state="checked|unchecked|indeterminate"] { /* styles for when checkbox is checked */ } [data-part="label"][data-state="checked|unchecked|indeterminate"] { /* styles for when checkbox is checked */ }
Focused State
When the checkbox 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 checkbox 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 checkbox 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 checkbox machine exposes the following context properties:
idsPartial<{ root: string; hiddenInput: string; control: string; label: string; }>The ids of the elements in the checkbox. Useful for composition.disabledbooleanWhether the checkbox is disabledinvalidbooleanWhether the checkbox is invalidrequiredbooleanWhether the checkbox is requiredcheckedCheckedStateThe controlled checked state of the checkboxdefaultCheckedCheckedStateThe initial checked state of the checkbox when rendered. Use when you don't need to control the checked state of the checkbox.readOnlybooleanWhether the checkbox is read-onlyonCheckedChange(details: CheckedChangeDetails) => voidThe callback invoked when the checked state changes.namestringThe name of the input field in a checkbox. Useful for form submission.formstringThe id of the form that the checkbox belongs to.valuestringThe value of checkbox input. Useful for form submission.dir"ltr" | "rtl"The document's text/writing direction.idstringThe unique identifier of the machine.getRootNode() => ShadowRoot | Node | DocumentA root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
Machine API
The checkbox api exposes the following methods:
checkedbooleanWhether the checkbox is checkeddisabledbooleanWhether the checkbox is disabledindeterminatebooleanWhether the checkbox is indeterminatefocusedbooleanWhether the checkbox is focusedcheckedStateCheckedStateThe checked state of the checkboxsetChecked(checked: CheckedState) => voidFunction to set the checked state of the checkboxtoggleCheckedVoidFunctionFunction to toggle the checked state of the checkbox
Data Attributes
Accessibility
Keyboard Interactions
- SpaceToggle the checkbox