Skip to content
Browse docs

Checkbox

Beta

A box that is checked, unchecked or mixed, and submits with forms.

---
import Checkbox from "@/components/ui/checkbox/Checkbox.astro";
---

<Checkbox checked>Remember me on this device</Checkbox>

Installation

npx eris-ui@latest add checkbox
pnpm dlx eris-ui@latest add checkbox
bunx eris-ui@latest add checkbox
yarn dlx eris-ui@latest add checkbox

Installs @zag-js/checkbox, @zag-js/vanilla.

How it works

The checkbox is a real <input type="checkbox"> inside a <label>, drawn with CSS. It toggles, shows its state and submits with a form before its script loads, and with JavaScript turned off. The script adds the mixed state and data-state, data-focus and data-hover attributes for styling.

States

---
import Checkbox from "@/components/ui/checkbox/Checkbox.astro";
---

<div class="flex flex-col items-start gap-4">
  <Checkbox indeterminate>Mixed</Checkbox>
  <Checkbox disabled>Disabled</Checkbox>
  <Checkbox disabled checked>
    Disabled and checked
  </Checkbox>
  <Checkbox readOnly checked>
    Read only
  </Checkbox>
  <Checkbox invalid>Invalid</Checkbox>
</div>

A readOnly checkbox keeps focus and is still sent with the form, but does not change. A disabled checkbox is skipped by Tab and left out of the form.

In a form

Give the checkbox a name. While it is checked, the form sends that name with value, which defaults to on. While it is unchecked, the form sends nothing for it. A form reset returns it to how it loaded.

---
import Checkbox from "@/components/ui/checkbox/Checkbox.astro";
import Button from "@/components/ui/button/Button.astro";
---

<form class="flex flex-col items-start gap-4">
  <Checkbox name="terms" value="accepted" required>
    I accept the terms
  </Checkbox>
  <Checkbox name="newsletter">Send me the newsletter</Checkbox>
  <div class="flex gap-3">
    <Button type="submit" size="sm">
      Sign up
    </Button>
    <Button type="reset" size="sm" variant="secondary">
      Reset
    </Button>
  </div>
</form>

Mixed state and select all

indeterminate starts a checkbox in the mixed state, shown as a dash. Clicking a mixed checkbox checks it.

To change the state from your own script, import setChecked from the component’s checkbox.ts and pass it the checkbox root and true, false or "indeterminate". It is safe to call before the checkbox has mounted.

setChecked fires no change event, the same as setting input.checked yourself. So a change event on the input always means the user did it.

---
import Checkbox from "@/components/ui/checkbox/Checkbox.astro";
---

<div class="flex flex-col items-start gap-3">
  <Checkbox id="demo-all">All notifications</Checkbox>
  <div id="demo-kinds" class="flex flex-col items-start gap-3 pl-8">
    <Checkbox checked>Comments</Checkbox>
    <Checkbox>Mentions</Checkbox>
    <Checkbox>Invites</Checkbox>
  </div>
</div>

<script>
  import { setChecked } from "@/components/ui/checkbox/checkbox";

  document.addEventListener("astro:page-load", () => {
    const all = document.querySelector<HTMLElement>('[data-eris-id="demo-all"]');
    const boxes = [...document.querySelectorAll<HTMLInputElement>("#demo-kinds input")];
    if (!all || !boxes.length) return;

    // Children changed: the parent becomes checked, unchecked or mixed.
    const sync = () => {
      const count = boxes.filter((box) => box.checked).length;
      setChecked(all, count === 0 ? false : count === boxes.length ? true : "indeterminate");
    };
    boxes.forEach((box) => box.addEventListener("change", sync));

    // Parent changed: every child follows it.
    all.querySelector("input")!.addEventListener("change", (event) => {
      const { checked } = event.target as HTMLInputElement;
      boxes.forEach((box) => setChecked(box.closest<HTMLElement>("[data-eris]")!, checked));
    });
    sync();
  });
</script>

Without visible text

When a checkbox has no text next to it, as in a table row, name it with label so screen readers can announce it.

---
import Checkbox from "@/components/ui/checkbox/Checkbox.astro";
---

<Checkbox label="Select row" />

Keyboard

Key Does
Tab Moves focus to the checkbox
Space Checks or unchecks the checkbox

Props

Prop Default Does
checked false Checked when the page loads
indeterminate false Starts in the mixed state
name none Field name sent with the form
value "on" Value sent while the checkbox is checked
disabled false Cannot be focused or changed, and is not submitted
readOnly false Can be focused and is submitted, but cannot be changed
required false The form will not submit while the box is unchecked
invalid false Marks the checkbox as failing validation
form none Id of a form elsewhere on the page
label none Accessible name when there is no visible text
id random Id for the instance, used to build the part ids