Skip to content

Checkbox

Use a checkbox for an option that stands on its own: a setting the user turns on or off, or one of several answers they can pick at the same time. If the choices are mutually exclusive, use radios; if the change applies the moment it is flipped rather than when a form is submitted, a Switch reads better.

checkbox/basic.tsx

Checkbox renders a <span> with role="checkbox" and a hidden <input> next to it. That means it carries no accessible name of its own — the Field.Label wrapped around it above is what gives it one, and every example on this page keeps that pattern. A Field generates the id, points the label at the control and carries the name into form submission, so none of that appears in the demo code.

Import

import { Checkbox } from "@forte-ui/react";

Examples

Sizes

size sets the painted box — 0.875rem, 1rem or 1.25rem. The mark scales with the box, and its stroke width is retuned at each size so the tick keeps the same optical weight rather than turning heavy on the small box and spindly on the large one. The clickable area does not shrink with the box: a pseudo-element on the root holds the hit target at 24×24px minimum (WCAG SC 2.5.8), so a sm checkbox is no harder to hit than an lg one.

checkbox/sizes.tsx

Indeterminate

indeterminate puts the checkbox in a mixed state — neither ticked nor unticked. The mark becomes a dash instead of a tick, aria-checked becomes "mixed", and data-indeterminate appears on both the root and the indicator, so either can be targeted from CSS (data-[indeterminate]:… in Tailwind).

It is a summary rather than a third value the user can choose: no activation ever produces a mixed box, so it is always derived from something else — most often a list that is only partly selected.

checkbox/indeterminate.tsx

indeterminate and checked are independent. A checkbox can be both, and when it is, the mixed state wins visually (the tick retracts, the dash draws in) while the hidden input still submits whatever checked says.

Groups

CheckboxGroup collects a series of checkboxes into one array of values, names them together and can disable them together. It has a page of its own: CheckboxGroup.

Disabled

disabled dims the box, blocks pointer and keyboard interaction, and sets aria-disabled. On its own it reaches the control and not the words next to it — so put it on the Field.Root instead, as the demo does. That takes precedence over the control's own prop and puts data-disabled on the label too, which is what keeps the reason an option is unavailable from sitting at full contrast beside a greyed-out box.

checkbox/disabled.tsx

Accessibility

Keyboard interactions
KeyBehaviour
TabMoves focus to the checkbox. Each checkbox is its own tab stop, including inside a CheckboxGroup.
SpaceToggles the focused checkbox. On a parent checkbox it drives the whole group — all, none, then back to the earlier partial selection.
EnterDoes not toggle. Inside a form it submits the form, matching a native checkbox.

The root exposes role="checkbox" and aria-checkedtrue, false, or "mixed" while indeterminate. The <input> beside it is aria-hidden and exists to carry the value into form submission, so name, value, required and form behave as they would on a native checkbox.

Motion. The mark is drawn on rather than faded in: both paths carry pathLength="1", and the stylesheet animates stroke-dashoffset from 1 to 0 over --forte-checkbox-draw-duration. That choice matters for reduced motion. The tick is information — for anyone who cannot rely on the fill colour it is the only thing separating on from off — so it must never be animated with a travel or scale token that collapses to zero. Under prefers-reduced-motion: reduce the library only shortens the durations (--forte-duration-normal drops from 240ms to 120ms), so the mark still appears; it just draws faster.

Under forced colours, the box paints with Canvas/CanvasText, a checked or mixed box with Highlight, and a disabled one with GrayText on Canvas — the tick stays visible in every state.

Theming

Every visual decision is a custom property declared on the checkbox root. Set them on the checkbox itself — through className or an inline style — rather than on an ancestor: the component declares its own defaults on the root element, and an element's own declaration beats an inherited value.

Theming tokens for Checkbox
PropertyControlsDefault
--forte-checkbox-sizeWidth and height of the painted box. size="sm" sets 0.875rem, size="lg" sets 1.25rem.1rem
--forte-checkbox-radiusCorner radius of the box.var(--forte-radius-2)
--forte-checkbox-border-widthBorder width.1px
--forte-checkbox-mark-widthStroke width of the tick and dash. Unitless on purpose — the value is in SVG user units inside a 16-unit viewBox, so the stroke scales with the box instead of thinning out at larger sizes. size retunes it: 2.25 for sm, 1.75 for lg.2
--forte-checkbox-durationColour transitions on the box and the fade of the indicator.var(--forte-duration-fast)
--forte-checkbox-draw-durationHow long the mark takes to draw in and out.var(--forte-duration-normal)
--forte-checkbox-bgFill while unchecked.var(--forte-color-background)
--forte-checkbox-bg-hoverFill on hover while unchecked.var(--forte-color-panel-hover)
--forte-checkbox-border-colorBorder colour while unchecked.var(--forte-color-border)
--forte-checkbox-border-color-hoverBorder colour on hover while unchecked.var(--forte-color-border-strong)
--forte-checkbox-fillFill while checked or indeterminate.var(--forte-color-primary)
--forte-checkbox-fill-hoverFill on hover while checked or indeterminate.var(--forte-color-primary-hover)
--forte-checkbox-mark-colorColour of the tick and dash — it becomes the root's color, and the marks stroke with currentColor.var(--forte-color-on-primary)

The three -hover values only ever apply on devices that report hover support, so a touch device never sticks in a hover colour after a tap.

tone reassigns --forte-checkbox-fill, --forte-checkbox-fill-hover and --forte-checkbox-mark-color. Inside a Field.Root, an invalid field overrides those and the border colours with the danger palette, so a validation failure never renders in a brand colour.

API reference

Checkbox

Props for Checkbox
PropTypeDefaultDescription
checkedbooleanundefinedWhether the checkbox is currently ticked. Pairs with `onCheckedChange` for a controlled checkbox; use `defaultChecked` for an uncontrolled one.
classNamestringAdditional class name(s). Applied after the internal styles so consumer utilities (e.g. Tailwind) win without needing `!important`.
defaultCheckedbooleanfalseWhether the checkbox starts ticked when uncontrolled.
indeterminatebooleanfalseMixed state: neither ticked nor unticked. Renders a dash instead of a tick and sets `data-indeterminate` on both the root and the indicator. Independent of `checked` — a checkbox that is indeterminate submits whatever `checked` says it submits.
indicatorClassNamestringAdditional class name(s) for the indicator — the element that holds the tick. Applied after the internal styles.
nativeButtonbooleanfalseSet to `true` when `render` replaces the root with a real `<button>`. The root is a `<span>` by default so that an enclosing `<label>` stays valid HTML; for the sibling-label pattern (`<label htmlFor>` + `id`) pass `nativeButton render={<button />}` instead. With `nativeButton`, `id` lands on the root element rather than on the hidden input.
parentbooleanfalseMarks this checkbox as the one that controls every other checkbox in the group. Only works inside a `CheckboxGroup` that has been given `value` and `allValues`; outside one it does nothing at all. The group drives the indeterminate state when only some children are ticked.
sizeCheckboxSizemdSize of the box. The tick scales with it, so the mark keeps its optical weight at every size.
toneCheckboxToneprimaryWhich semantic colour set the checked fill draws from. Inside a `Field.Root` an invalid field overrides this with the danger palette, so a validation error always reads as an error.

Anything Base UI's Checkbox.Root accepts passes through as well — including name, value, uncheckedValue, onCheckedChange, disabled, readOnly, required, form, inputRef and render.

CheckboxGroup

See CheckboxGroup for the group's own prop table, its theming property and the parent-checkbox rules.