Skip to main content
View Zag.js on Github
Join the Discord server


A checkbox allows users to make a binary choice, i.e. a choice between one of two possible mutually exclusive options.



  • Tri-state checkbox. i.e. indeterminate state
  • Syncs with disabled state of fieldset
  • Syncs with form reset events
  • Can be toggled programmatically


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

This command will install the framework agnostic checkbox logic and the reactive utilities for your framework of choice.


To set up the checkbox 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.

On a high level, the checkbox consists of:

  • Root: The root container for the checkbox
  • Label: The label that gives the user information on the checkbox
  • Control: The element that visually represents the checkbox.
  • Input: The native html input that is visually hidden.


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" function Checkbox() { const [state, send] = useMachine(checkbox.machine({ id: "1" })) const api = checkbox.connect(state, send, normalizeProps) return ( <label {...api.rootProps}> <span {...api.labelProps}> Input is {api.isChecked ? "checked" : "unchecked"} </span> <input {...api.inputProps} /> <div {...api.controlProps} /> </label> ) }

Indeterminate checkboxes

To make a checkbox indeterminate, set the context's indeterminate property to true

const [state, send] = useMachine( checkbox.machine({ indeterminate: true, }), )

Disabling the checkbox

To make a checkbox disabled, set the context's disabled property to true

const [state, send] = useMachine( checkbox.machine({ disabled: true, }), )

Making the checkbox readonly

To make a checkbox readonly, set the context's readOnly property to true

const [state, send] = useMachine( checkbox.machine({ readOnly: true, }), )

Making it checked by default

To make a checkbox checked by default, set the context's defaultChecked property to true

const [state, send] = useMachine( checkbox.machine({ defaultChecked: true, }), )

Listening for changes

When the checkbox value changes, the onChange callback is invoked.

const [state, send] = useMachine( checkbox.machine({ onChange({ checked }) { console.log("checkbox is:", value) // 'checked' | 'unchecked' | 'indeterminate' }, }), )

Methods and Properties

The checkbox's api provides properties and methods you can use to programmatically read and set the checkbox's value.

isCheckedWhether the checkbox is checked
isDisabledWhether the checkbox is disabled
isIndeterminateWhether the checkbox is indetermintae
isFocusedWhether the checkbox is focused
isReadOnlyWhether the checkbox is readonly
viewCurrent visual state of checkbox, to ease rendering accordingly
setCheckedFunction used to set the checked state.
setIndeterminateFunction used to set the indeterminate state.
const api = connect(state, send) api.view // 'checked' | 'unchecked' | 'mixed' api.setChecked(true) api.isChecked // true api.setIndeterminate(true) api.isIndeterminate // true

Usage within forms

To use checkbox 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( checkbox.machine({ name: "fruits", }), )

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.

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 */ }

Edit this page on GitHub

On this page