Checkbox
A form checkbox input with text, supporting checked, unchecked, and indeterminate states. Built on a native <input type="checkbox"> for full form compatibility and accessibility.
import { Checkbox } from "@tenstorrent/vesper/checkbox";
export default function CheckboxDemo() {
return <Checkbox text="Accept terms and conditions" />;
}Options
| Prop | Type | Description | Default |
|---|---|---|---|
text | string | The text displayed next to the checkbox, also used as the input's default aria-label. Required. | — |
size | "sm" | "md" | The size of the checkbox and its text. | "md" |
indeterminate | boolean | When true, renders the checkbox in an indeterminate (mixed) state with a dash icon. | false |
checked | boolean | The checked state of the checkbox (controlled mode). | — |
defaultChecked | boolean | The initial checked state (uncontrolled mode). | false |
onChange | ChangeEventHandler<HTMLInputElement> | Callback fired when the checked state changes. | — |
name | string | The form field name submitted with form data. | — |
value | string | The value submitted with form data when the checkbox is checked. | "on" |
form | string | Associates the checkbox with a <form> element by its id. | — |
id | string | An identifier applied to the underlying <input>, used to reference it from form validation errors. | — |
disabled | boolean | When true, prevents interaction. | false |
required | boolean | When true, marks the input as required and appends an asterisk to the text. | false |
ref | Ref<HTMLInputElement> | A ref forwarded to the underlying <input> element. | — |
Form and accessibility props such as autoFocus, tabIndex, aria-label, aria-labelledby, aria-describedby, aria-invalid, and event handlers like onFocus, onBlur, and onKeyDown are forwarded to the underlying <input> element, as is ref. Any other props, including className, are forwarded to the wrapping <label> element.
Use a Checkbox for an individual boolean choice, such as accepting terms or opting into a mailing list.
When the user is picking any number of options from a set, reach for the Choicebox component in multiselect mode instead of rendering a list of checkboxes.
For other related alternatives, use the Switch component when toggling a setting on and off takes effect immediately, the RadioGroup component when exactly one option out of many must be chosen, and the Toggle component when a compact segmented control fits better than a list.
Examples
Uncontrolled vs controlled
Render a Checkbox in an uncontrolled fashion to let it keep track of its own checked state. Pass defaultChecked if it should start out checked:
import { Checkbox } from "@tenstorrent/vesper/checkbox";
export default function UncontrolledCheckbox() {
return <Checkbox text="Sign up for our newsletter" defaultChecked />;
}If you need to control whether the checkbox is checked, you can do so via the checked and onChange props. The new state is read from the event's target.checked:
import { useState } from "react";
import { Checkbox } from "@tenstorrent/vesper/checkbox";
export default function ControlledCheckbox() {
const [checked, setChecked] = useState(false);
return (
<Checkbox
text="Sign up for our newsletter"
checked={checked}
onChange={(e) => setChecked(e.target.checked)}
/>
);
}Different sizes
The Checkbox component can be rendered at sm or md size, defaulting to md. Size affects the dimensions of the checkbox itself, as well as the text styles of the text rendered beside it.
import { Checkbox } from "@tenstorrent/vesper/checkbox";
export default function CheckboxSizes() {
return (
<div
style={{
display: "flex",
flexDirection: "column",
gap: "var(--vesper-spacing-4)",
}}
>
<Checkbox size="sm" text="A small checkbox" />
<Checkbox size="md" text="A medium checkbox" />
</div>
);
}Indeterminate state
Pass indeterminate to render the checkbox in a mixed state, displayed as a dash instead of a checkmark:
import { Checkbox } from "@tenstorrent/vesper/checkbox";
export default function CheckboxStates() {
return (
<div
style={{
display: "flex",
flexDirection: "column",
gap: "var(--vesper-spacing-4)",
}}
>
<Checkbox text="Unchecked" />
<Checkbox text="Checked" defaultChecked />
<Checkbox text="Indeterminate" indeterminate />
</div>
);
}indeterminate is a property of the underlying <input> rather than an attribute, so it is set for you whenever the prop changes. It is purely a display state: it takes visual precedence over checked, but it does not change the value submitted with form data, and it is announced as "mixed" by assistive technology.Required fields
Passing required marks the input as required for native form validation, and appends an asterisk to the text:
import { Checkbox } from "@tenstorrent/vesper/checkbox";
export default function RequiredCheckbox() {
// asterisk is appended to the text
return <Checkbox text="I agree to the terms and conditions" required />;
}Disabling the checkbox
You can disable a Checkbox by passing disabled={true} or just disabled as a prop. A disabled checkbox still displays its current state, but it cannot be toggled by pointer or keyboard, is skipped in the tab order, and is excluded from form data:
import { Checkbox } from "@tenstorrent/vesper/checkbox";
export default function DisabledCheckboxes() {
return (
<div
style={{
display: "flex",
flexDirection: "column",
gap: "var(--vesper-spacing-4)",
}}
>
<Checkbox text="Sign up for our newsletter" disabled />
<Checkbox text="Sign up for our newsletter" defaultChecked disabled />
<Checkbox text="Sign up for our newsletter" indeterminate disabled />
</div>
);
}Usage in forms
Checkbox renders a native <input type="checkbox">, so it works with regular form submission and native validation via props like name, value, form, and required. A checked checkbox submits its value (defaulting to "on"), and an unchecked one is omitted from form data entirely.
Pass required when the checkbox must be checked before the form can be submitted, such as a terms and conditions agreement:
import { useState } from "react";
import { Button } from "@tenstorrent/vesper/button";
import { Checkbox } from "@tenstorrent/vesper/checkbox";
import { Typography } from "@tenstorrent/vesper/typography";
export default function FormCheckboxDemo() {
const [submitted, setSubmitted] = useState<string | null>(null);
return (
<form
style={{
display: "flex",
flexDirection: "column",
gap: "var(--vesper-spacing-4)",
}}
onSubmit={(event) => {
event.preventDefault();
const data = new FormData(event.currentTarget);
setSubmitted(String(data.get("terms") ?? ""));
}}
>
<Checkbox
required
name="terms"
value="accepted"
text="I agree to the terms and conditions"
/>
<Button size="sm" type="submit">
Submit
</Button>
{submitted !== null && (
<Typography variant="copy-sm">
Submitted value: terms={submitted}
</Typography>
)}
</form>
);
}The form above submits terms=accepted once the box is checked, and refuses to submit at all while it is unchecked.
Use the form prop to associate the field with a <form> rendered elsewhere on the page:
import { Button } from "@tenstorrent/vesper/button";
import { Checkbox } from "@tenstorrent/vesper/checkbox";
export default function CheckboxFormPropDemo() {
return (
<div>
<form id="signup" action="/api/signup" method="post">
<Button type="submit">Create account</Button>
</form>
{/* rendered outside of the form, but submitted with it */}
<Checkbox
required
form="signup"
name="terms"
value="accepted"
text="I agree to the terms"
/>
;
</div>
);
}required only applies to the checkbox it is passed to, and a checkbox cannot express a rule that spans several fields.
To submit a set of options under one name, and to require a certain number of them to be selected, use the Choicebox component in multiselect mode with its min and max props.Accessing the underlying element
Use the ref prop when you need direct access to the underlying <input> element, eg. to focus it or report a custom validation message:
import { useRef } from "react";
import { Checkbox } from "@tenstorrent/vesper/checkbox";
export default function CheckboxRefDemo() {
const ref = useRef<HTMLInputElement>(null);
return (
<Checkbox
required
text="I agree to the terms"
ref={ref}
onInvalid={() =>
ref.current?.setCustomValidity("Please accept the terms to continue.")
}
onChange={() => ref.current?.setCustomValidity("")}
/>
);
}