Checkbox

Allow users to switch between checked, unchecked, and indeterminate states.

llms.txt
	<script lang="ts">
  import { Checkbox, Label } from "bits-ui";
  import Check from "phosphor-svelte/lib/Check";
  import Minus from "phosphor-svelte/lib/Minus";
</script>
 
<div class="flex items-center space-x-3">
  <Checkbox.Root
    id="terms"
    aria-labelledby="terms-label"
    class="border-muted bg-foreground data-[state=unchecked]:border-border-input data-[state=unchecked]:bg-background data-[state=unchecked]:hover:border-dark-40 peer inline-flex size-[25px] items-center justify-center rounded-md border transition-all duration-150 ease-in-out active:scale-[0.98]"
    name="hello"
    indeterminate
  >
    {#snippet children({ checked, indeterminate })}
      <div class="text-background inline-flex items-center justify-center">
        {#if indeterminate}
          <Minus class="size-[15px]" weight="bold" />
        {:else if checked}
          <Check class="size-[15px]" weight="bold" />
        {/if}
      </div>
    {/snippet}
  </Checkbox.Root>
  <Label.Root
    id="terms-label"
    for="terms"
    class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
  >
    Accept terms and conditions
  </Label.Root>
</div>

Overview

The Checkbox component provides a flexible and accessible way to create checkbox inputs in your Svelte applications. It supports three states: checked, unchecked, and indeterminate, allowing for complex form interactions and data representations.

Key Features

  • Tri-State Support: Handles checked, unchecked, and indeterminate states, providing versatility in form design.
  • Accessibility: Built with WAI-ARIA guidelines in mind, ensuring keyboard navigation and screen reader support.
  • Flexible State Management: Supports both controlled and uncontrolled state, allowing for full control over the checkbox's checked state.

Architecture

The Checkbox component is composed of the following parts:

  • Root: The main component that manages the state and behavior of the checkbox.

Structure

Here's an overview of how the Checkbox component is structured in code:

	<script lang="ts">
  import { Checkbox } from "bits-ui";
</script>
 
<Checkbox.Root>
  {#snippet children({ checked, indeterminate })}
    {#if indeterminate}
      -
    {:else if checked}

    {:else}

    {/if}
  {/snippet}
</Checkbox.Root>

Reusable Components

It's recommended to use the Checkbox primitive to create your own custom checkbox component that can be used throughout your application. In the example below, we're using the Checkbox and Label components to create a custom checkbox component.

MyCheckbox.svelte
	<script lang="ts">
  import {
    Checkbox,
    Label,
    useId,
    type WithoutChildrenOrChild,
  } from "bits-ui";
 
  let {
    id = useId(),
    checked = $bindable(false),
    ref = $bindable(null),
    labelRef = $bindable(null),
    ...restProps
  }: WithoutChildrenOrChild<Checkbox.RootProps> & {
    labelText: string;
    labelRef?: HTMLLabelElement | null;
  } = $props();
</script>
 
<Checkbox.Root bind:checked bind:ref {...restProps}>
  {#snippet children({ checked, indeterminate })}
    {#if indeterminate}
      -
    {:else if checked}

    {:else}

    {/if}
  {/snippet}
</Checkbox.Root>
<Label.Root for={id} bind:ref={labelRef}>
  {labelText}
</Label.Root>

You can then use the MyCheckbox component in your application like so:

+page.svelte
	<script lang="ts">
  import MyCheckbox from "$lib/components/MyCheckbox.svelte";
</script>
 
<MyCheckbox labelText="Enable notifications" />

Managing Checked State

This section covers how to manage the checked state of the Checkbox.

Two-Way Binding

Use bind:checked for simple, automatic state synchronization:

	<script lang="ts">
  import { Checkbox } from "bits-ui";
  let myChecked = $state(false);
</script>
 
<button onclick={() => (myChecked = false)}> uncheck </button>
 
<Checkbox.Root bind:checked={myChecked} />

Fully Controlled

Use a Function Binding for complete control over the state's reads and writes.

	<script lang="ts">
  import { Checkbox } from "bits-ui";
  let myChecked = $state(false);
 
  function getChecked() {
    return myChecked;
  }
 
  function setChecked(newChecked: boolean) {
    myChecked = newChecked;
  }
</script>
 
<Checkbox.Root bind:checked={getChecked, setChecked}>
  <!-- ... -->
</Checkbox.Root>

Managing Indeterminate State

This section covers how to manage the indeterminate state of the Checkbox.

Two-Way Binding

Use bind:indeterminate for simple, automatic state synchronization:

	<script lang="ts">
  import MyCheckbox from "$lib/components/MyCheckbox.svelte";
  let myIndeterminate = $state(true);
</script>
 
<button onclick={() => (myIndeterminate = false)}>
  clear indeterminate
</button>
 
<MyCheckbox bind:indeterminate={myIndeterminate} />

Fully Controlled

Use a Function Binding for complete control over the state's reads and writes.

	<script lang="ts">
  import { Checkbox } from "bits-ui";
  let myIndeterminate = $state(true);
 
  function getIndeterminate() {
    return myIndeterminate;
  }
 
  function setIndeterminate(newIndeterminate: boolean) {
    myIndeterminate = newIndeterminate;
  }
</script>
 
<Checkbox.Root bind:indeterminate={getIndeterminate, setIndeterminate}>
  <!-- ... -->
</Checkbox.Root>

Disabled State

You can disable the checkbox by setting the disabled prop to true.

	<MyCheckbox disabled labelText="Enable notifications" />

HTML Forms

If you set the name prop, a hidden checkbox input will be rendered to submit the value of the checkbox with a form.

By default, the checkbox will be submitted with default checkbox value of 'on' if the checked prop is true.

	<MyCheckbox name="notifications" labelText="Enable notifications" />

Custom Input Value

If you'd prefer to submit a different value, you can use the value prop to set the value of the hidden input.

For example, if you wanted to submit a string value, you could do the following:

	<MyCheckbox
  value="hello"
  name="notifications"
  labelText="Enable notifications"
/>

Required

If you want to make the checkbox required, you can use the required prop.

	<Checkbox.Root required>
  <!-- ... -->
</Checkbox.Root>

This will apply the required attribute to the hidden input element, ensuring that proper form submission is enforced.

Checkbox Groups

You can use the Checkbox.Group component to create a checkbox group.

	<script lang="ts">
  import { Checkbox } from "bits-ui";
</script>
 
<Checkbox.Group name="notifications">
  <Checkbox.GroupLabel>Notifications</Checkbox.GroupLabel>
  <Checkbox.Root value="marketing" />
  <Checkbox.Root value="promotions" />
  <Checkbox.Root value="news" />
</Checkbox.Group>
Notifications
	<script lang="ts">
  import { Checkbox, Label, useId } from "bits-ui";
  import Check from "phosphor-svelte/lib/Check";
  import Minus from "phosphor-svelte/lib/Minus";
 
  let myValue = $state<string[]>(["marketing", "news"]);
</script>
 
<Checkbox.Group
  class="flex flex-col gap-3"
  bind:value={myValue}
  name="notifications"
>
  <Checkbox.GroupLabel class="text-foreground-alt text-sm font-medium">
    Notifications
  </Checkbox.GroupLabel>
  <div class="flex flex-col gap-4">
    {@render MyCheckbox({ label: "Marketing", value: "marketing" })}
    {@render MyCheckbox({ label: "Promotions", value: "promotions" })}
    {@render MyCheckbox({ label: "News", value: "news" })}
    {@render MyCheckbox({ label: "Updates", value: "updates" })}
  </div>
</Checkbox.Group>
 
{#snippet MyCheckbox({ value, label }: { value: string; label: string })}
  {@const id = useId()}
  <div class="flex items-center">
    <Checkbox.Root
      {id}
      aria-labelledby="{id}-label"
      class="border-muted bg-foreground data-[state=unchecked]:border-border-input data-[state=unchecked]:bg-background data-[state=unchecked]:hover:border-dark-40 peer inline-flex size-[25px] items-center justify-center rounded-md border transition-all duration-150 ease-in-out active:scale-[0.98]"
      name="hello"
      {value}
    >
      {#snippet children({ checked, indeterminate })}
        <div class="text-background inline-flex items-center justify-center">
          {#if indeterminate}
            <Minus class="size-[15px]" weight="bold" />
          {:else if checked}
            <Check class="size-[15px]" weight="bold" />
          {/if}
        </div>
      {/snippet}
    </Checkbox.Root>
    <Label.Root
      id="{id}-label"
      for={id}
      class="pl-3 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
    >
      {label}
    </Label.Root>
  </div>
{/snippet}

Managing Value State

This section covers how to manage the value state of a Checkbox Group.

Two-Way Binding

Use bind:value for simple, automatic state synchronization:

	<script lang="ts">
  import { Checkbox } from "bits-ui";
  let myValue = $state<string[]>([]);
</script>
 
<button
  onclick={() => {
    myValue = ["item-1", "item-2"];
  }}
>
  Open Items 1 and 2
</button>
 
<Checkbox.Group name="myItems" bind:value={myValue}>
  <Checkbox.GroupLabel>Items</Checkbox.GroupLabel>
  <Checkbox.Root value="item-1" />
  <Checkbox.Root value="item-2" />
  <Checkbox.Root value="item-3" />
</Checkbox.Group>

Fully Controlled

Use a Function Binding for complete control over the state's reads and writes.

	<script lang="ts">
  import { Checkbox } from "bits-ui";
  let myValue = $state<string[]>([]);
 
  function getValue() {
    return myValue;
  }
 
  function setValue(newValue: string[]) {
    myValue = newValue;
  }
</script>
 
<Checkbox.Group bind:value={getValue, setValue}>
  <!-- ... -->
</Checkbox.Group>

HTML Forms

To render hidden <input /> elements for the various checkboxes within a group, pass a name to Checkbox.Group. All descendent checkboxes will then render hidden inputs with the same name.

	<Checkbox.Group name="notifications">
  <!-- ... -->
</Checkbox.Group>

When a Checkbox.Group component is used, its descendent Checkbox.Root components will use certain properties from the group, such as the name, required, and disabled.

API Reference

Checkbox.Root

The button component used to toggle the state of the checkbox.

Property Details
checked
onCheckedChange
indeterminate
onIndeterminateChange
disabled
required
name
value
ref
children
child
Data Attribute Details
data-state
data-disabled
data-checkbox-root

Checkbox.Group

A group that synchronizes its value state with its descendant checkboxes.

Property Details
value
onValueChange
disabled
required
name
ref
children
child
Data Attribute Details
data-disabled
data-checkbox-group

Checkbox.GroupLabel

An accessible label for the checkbox group.

Property Details
ref
children
child
Data Attribute Details
data-disabled
data-checkbox-group-label