implement
Checkbox

Checkbox

A control that toggles between checked, unchecked, and indeterminate.

import { Div, Label } from "@implementjs/core";
import { Checkbox } from "@/lib/components/ui/checkbox";

export default function CheckboxDemo() {
	return Div(
		{ class: "flex flex-col gap-4" },
		Div(
			{ class: "flex items-center gap-2" },
			Checkbox({ id: "terms" }),
			Label(
				{ for: "terms", class: "text-sm leading-none font-medium" },
				"Accept terms and conditions",
			),
		),
		Div(
			{ class: "flex items-center gap-2" },
			Checkbox({ id: "emails", checked: true }),
			Label(
				{ for: "emails", class: "text-sm leading-none font-medium" },
				"Send me product updates",
			),
		),
		Div(
			{ class: "flex items-center gap-2" },
			Checkbox({ id: "partial", indeterminate: true }),
			Label(
				{ for: "partial", class: "text-sm leading-none font-medium" },
				"Select all notifications",
			),
		),
		Div(
			{ class: "flex items-center gap-2" },
			Checkbox({ id: "disabled", checked: true, disabled: true }),
			Label(
				{
					for: "disabled",
					class:
						"text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
				},
				"Disabled",
			),
		),
	);
}

A checkbox is a button that turns a value on and off. Checkbox renders a Button with role="checkbox" and keeps aria-checked in sync — you give it a size, a border, and the indicator that shows inside it.

import { Checkbox } from "@implementjs/primitives";

Checkbox();

It accepts optional props and children — pass a props object when you need attributes, or pass children directly. See createComponent. Extra props are forwarded onto the underlying Button.

Checked state

checked defaults to false. Pass a boolean to seed it, or a signal to control it from outside (signal() returns a writable unchanged, so the same prop accepts both):

const accepted = signal(false);

Checkbox({ checked: accepted });

Button({ onClick: () => accepted.set(true) }, "Accept");

Clicking the checkbox toggles checked. Space and Enter do the same, because it is a real Button.

Indeterminate

indeterminate is for a parent that represents a mixed set — some children on, some off. Pass a boolean or a signal. While it is true, data-state is "indeterminate" and aria-checked is "mixed".

Clicking an indeterminate checkbox clears that flag and checks it:

const all = signal(false);
const mixed = signal(true);

Checkbox({ checked: all, indeterminate: mixed });

Labels

Pair it with a Label whose for matches the checkbox id. Clicking the text then toggles the control, and the accessible name comes from the label instead of the indicator:

import { Label } from "@implementjs/core";

Div(
	{ class: "flex items-center gap-2" },
	Checkbox({ id: "terms" }),
	Label({ for: "terms" }, "Accept terms and conditions"),
);

Forms

Pass name and the checkbox renders a visually hidden native input that submits with the form. The value is "on" while checked; pass value to change it. required and disabled apply to that input too:

Form(
	{ method: "post" },
	Checkbox({ name: "terms", value: "accepted" }),
	Button({ type: "submit" }, "Continue"),
);

Without name, no hidden input is rendered. An indeterminate checkbox submits as unchecked.

Styling

The primitive is unstyled until you give it a look. It sets data-checkbox-root and data-state as "checked", "unchecked", or "indeterminate", so one class list can cover every state. Children are the indicator — typically a check icon that you show while checked, and a minus while mixed:

import { CheckIcon, MinusIcon } from "@implementjs/lucide";

Checkbox(
	{
		class:
			"size-4 rounded-[4px] border border-input data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground",
	},
	CheckIcon({ class: "size-3.5 hidden [[data-state=checked]_&]:block" }),
	MinusIcon({ class: "size-3.5 hidden [[data-state=indeterminate]_&]:block" }),
);

data-state is there for the fill, the icon, and anything else that should react to checked versus mixed without you threading a signal through.

API Reference

Checkbox

A toggle that is checked, unchecked, or indeterminate. Sets role="checkbox" and aria-checked. Give it a look and an indicator; it handles the state. Renders a Button; extra props are forwarded onto it.

PropTypeDefaultDescription
checkedSignal<boolean> | booleanfalseThe checked state. Pass a signal to control it from outside; a boolean seeds uncontrolled state.
indeterminateSignal<boolean> | booleanfalsePartial selection. While true, data-state is "indeterminate" and aria-checked is "mixed". A click clears it and checks the box.
namestringIf set, a hidden checkbox is rendered so the value submits with a form.
valuestring"on"The value submitted while checked. Only used when name is set.
requiredbooleanfalseMarks the hidden input as required. Sets aria-required on the button.
Data attributeValue
[data-checkbox-root]Present
[data-state]"checked" | "unchecked" | "indeterminate"