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; }> | undefinedThe ids of the elements in the checkbox. Useful for composition.disabledboolean | undefinedWhether the checkbox is disabledinvalidboolean | undefinedWhether the checkbox is invalidrequiredboolean | undefinedWhether the checkbox is requiredcheckedCheckedState | undefinedThe controlled checked state of the checkboxdefaultCheckedCheckedState | undefinedThe initial checked state of the checkbox when rendered. Use when you don't need to control the checked state of the checkbox.readOnlyboolean | undefinedWhether the checkbox is read-onlyonCheckedChange((details: CheckedChangeDetails) => void) | undefinedThe callback invoked when the checked state changes.namestring | undefinedThe name of the input field in a checkbox. Useful for form submission.formstring | undefinedThe id of the form that the checkbox belongs to.valuestring | undefinedThe value of checkbox input. Useful for form submission.dir"ltr" | "rtl" | undefinedThe document's text/writing direction.idstringThe unique identifier of the machine.getRootNode(() => ShadowRoot | Node | Document) | undefinedA 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 checkeddisabledboolean | undefinedWhether the checkbox is disabledindeterminatebooleanWhether the checkbox is indeterminatefocusedboolean | undefinedWhether 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