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. i.e.
indeterminatestate - Syncs with
disabledstate of fieldset - Syncs with form
resetevents - Can be toggled programmatically
Installation
To use the checkbox machine in your project, run the following command in your command line:
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
To set up the checkbox 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.
Usage
First, import the checkbox package into your project
import * as checkbox from "@zag-js/checkbox"
The checkbox package exports two key functions:
machine— The state machine logic for the checkbox 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 checkbox machine in your project 🔥
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
To make a checkbox checked by default, set the context's defaultChecked
property to true
const service = useMachine(checkbox.machine, { defaultChecked: true, })
Indeterminate checkboxes
To make a checkbox indeterminate, set the defaultChecked or checked property
to "indeterminate"
const service = useMachine(checkbox.machine, { defaultChecked: "indeterminate", })
Controlled checkbox
To control the checked state programmatically, pass the checked and
onCheckedChange properties to the machine function.
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, { get checked() { return checked() }, onCheckedChange(details) { setChecked(details.checked) }, }) return ( // ... ) }
<script setup lang="ts"> import { ref } from "vue" const checkedRef = ref(false) const service = useMachine(checkbox.machine, { get checked() { return checkedRef.value }, onCheckedChange(details) { checkedRef.value = details.checked }, }) </script>
<script lang="ts"> let checked = $state(false) const service = useMachine(checkbox.machine, { get checked() { return checked }, onCheckedChange(details) { checked = details.checked }, }) </script>
Disabling the checkbox
To make a checkbox disabled, set the context's disabled property to true
const service = useMachine(checkbox.machine, { disabled: true, })
Listening for changes
When the checkbox value changes, the onCheckChange callback is invoked.
const service = useMachine(checkbox.machine, { onCheckChange(details) { // details => { checked: boolean } console.log("checkbox is:", details.checked) }, })
Usage within forms
To use checkbox within forms, use the exposed api.getHiddenInputProps() from
the connect function and ensure you pass name value to the machine's
context.
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()} />
Styling guide
Earlier, we mentioned that each checkbox part has a data-part attribute added
to them to select and style them in the DOM.
Checked state
When the checkbox input is checked, the data-checked 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
Edit this page on GitHub