Theme Switcher

Checkbox

A form control that allows the user to toggle between checked and not checked. Supports indeterminate state.

import { Checkbox } from "@ngrok/mantle/checkbox";
import { Label } from "@ngrok/mantle/label";

<Label htmlFor="terms" className="flex items-center gap-2">
	<Checkbox name="terms" id="terms" />
	Accept terms and conditions
</Label>
<Label htmlFor="unchecked" className="flex items-center gap-2">
	<Checkbox id="unchecked" name="unchecked" checked={false} />
	Unchecked
</Label>
<Label htmlFor="checked" className="flex items-center gap-2">
	<Checkbox id="checked" name="checked" checked />
	Checked
</Label>
<Label htmlFor="indeterminate" className="flex items-center gap-2">
	<Checkbox id="indeterminate" name="indeterminate" checked="indeterminate" />
	Indeterminate
</Label>

Examples

Checkbox in a form with client-side validation

In this example, the Checkbox is used in a form with client-side validation. The form is built using @tanstack/react-formand zod for validation. The form accepts and validates the input before submission.

import { Button } from "@ngrok/mantle/button";
import { Label } from "@ngrok/mantle/label";
import { Checkbox } from "@ngrok/mantle/checkbox";
import { useForm } from "@tanstack/react-form";
import { z } from "zod";
import { useSubmit } from "react-router";

const formSchema = z.object({
	acceptedTermsAndConditions: z
		.boolean()
		.refine((value) => value, "You must accept the terms and conditions."),
});

type FormValues = z.infer<typeof formSchema>;

function FormExample() {
	const submit = useSubmit();
	const form = useForm({
		defaultValues: {
			acceptedTermsAndConditions: false,
		} satisfies FormValues as FormValues,
		validators: {
			onSubmit: formSchema,
		},
		onSubmit: ({ value }) => {
			// Handle form submission here
			submit(value, {
				method: "post",
			});
		},
	});

	return (
		<form
			className="space-y-4"
			onSubmit={(event) => {
				event.preventDefault();
				event.stopPropagation();
				void form.handleSubmit();
			}}
		>
			<div className="space-y-1">
				<form.Field name="acceptedTermsAndConditions">
					{(field) => (
						<Label htmlFor={field.name} className="flex items-center gap-2">
							<Checkbox
								name={field.name}
								id={field.name}
								checked={field.state.value}
								onBlur={field.handleBlur}
								onChange={(event) => field.handleChange(event.target.checked)}
								validation={field.state.meta.errors.length > 0 && "error"}
							/>
							Accept terms and conditions
						</Label>
					)}
				</form.Field>
				<form.Field name="acceptedTermsAndConditions">
					{(field) =>
						field.state.meta.errors.map((error) => (
							<p
								key={error?.message}
								className="text-sm leading-4 text-danger-600"
							>
								{error?.message}
							</p>
						))
					}
				</form.Field>
			</div>
			<form.Subscribe selector={(state) => state.isDirty}>
				{(isDirty) => (
					<Button type="submit" appearance="filled" disabled={!isDirty}>
						Submit
					</Button>
				)}
			</form.Subscribe>
		</form>
	);
}

API Reference

The Checkbox accepts the following props in addition to the standard HTML checkbox input attributes.

PropTypeDefaultDescription
checked
  • boolean
  • indeterminate

Whether the checkbox is checked or not. Setting this to indeterminate will show the indeterminate state. This is useful for when the checkbox is in a parent-child relationship, but this requires manual, controlled state.

defaultChecked
  • boolean
  • indeterminate

The checked state of the checkbox when it is initially rendered. Use when you do not need to control its checked state.

validation
  • error
  • success
  • warning
  • false
  • () => "error" | "success" | "warning" | false

Use the validation prop to show if the checkbox has a specific validation status. This will change the border and outline of the checkbox.

The false type is useful when using short-circuiting logic so that you don't need to use a ternary with undefined.

Setting validation to error also sets aria-invalid.