Checkbox
Checkbox with a label, an indeterminate state and an optional help button. It is the control for a choice the user confirms later, by submitting the form around it.
Use this when / not when
- Use for one or more independent choices in a form: a list of permissions, a "remember me", the select-all of a list.
- Not for a setting that takes effect the moment it is clicked — that reads as a switch, and
is
ToggleButton. - Not for one choice out of several:
RadioButtonGroup. - Not to give the choice a field label and an error message — wrap it in
FieldContainer, which this component'shasErrorexpects.
Import
import { Checkbox } from "@onlyoffice/apps-ui-kit/components/checkbox";
Also exported from the root barrel @onlyoffice/apps-ui-kit.
Needs ThemeProvider from @onlyoffice/apps-ui-kit/providers/theme above it in the tree.
Without it the box renders in the light palette and ignores the dark theme.
Stories
Default
A single checkbox with a label, the starting point for any form choice; click it to tick it, and change any other prop live in the Controls panel below.
Checked States
The two states a user switches between: Checked starts ticked (isChecked) and Unchecked starts empty; clicking either flips it.
Indeterminate States
For a select-all whose children are only partly selected: Indeterminate shows a filled square instead of a tick (isIndeterminate), and Disabled Indeterminate is the same square greyed out (isDisabled).
Disabled States
For a choice the user cannot change right now: each state keeps its mark but the box and the label turn grey and clicks are ignored (isDisabled).
Error States
For a required choice the form rejected: the box border and the label turn the error colour, checked or not (hasError); the message itself comes from a wrapping FieldContainer.
With Truncation
For a label longer than the space it gets: in a 200px container the text stays on one line and ends with an ellipsis (truncate) instead of wrapping.
With Title
For a short explanation the label has no room for: rest the pointer on the checkbox to open the tooltip (title). The tooltip is the kit's shared one, so the app must mount RootTooltip once, as this story does.
With Help Button
For a choice that needs more explanation than its label: an info icon follows the label (helpButton); click it to read the hint, and the checkbox stays as it was. A HelpButton with a text hint uses the shared tooltip, so RootTooltip is mounted here too.
Css Customization
The variables are listed under CSS variables on this page. The example is one checked box on a wrapper that sets every overridable one, so it shows them all at once: the wider gap, the taller row, the light fill, the blue border and the blue tick.
Minimal example
import { useState } from "react";
import { Checkbox } from "@onlyoffice/apps-ui-kit/components/checkbox";
export function TermsCheckbox() {
const [accepted, setAccepted] = useState(false);
return (
<Checkbox
label="I accept the terms"
isChecked={accepted}
tabIndex={0}
onChange={(e) => setAccepted(e.target.checked)}
/>
);
}
Props
Inherited from TextProps
Declared by components/text and accepted here too.
Recipes
Disabled
import { Checkbox } from "@onlyoffice/apps-ui-kit/components/checkbox";
export function ManagedSetting({ enabled }: { enabled: boolean }) {
return (
<Checkbox
label="Managed by your administrator"
isChecked={enabled}
isDisabled
onChange={() => {}}
/>
);
}
Select all, with an indeterminate state
import { useState } from "react";
import { Checkbox } from "@onlyoffice/apps-ui-kit/components/checkbox";
const ROOMS = ["Design", "Development", "Marketing"];
export function RoomPicker() {
const [selected, setSelected] = useState<string[]>([]);
const toggle = (room: string, on: boolean) =>
setSelected((current) =>
on ? [...current, room] : current.filter((item) => item !== room),
);
return (
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
<Checkbox
label="All rooms"
tabIndex={0}
isChecked={selected.length === ROOMS.length}
isIndeterminate={selected.length > 0 && selected.length < ROOMS.length}
onChange={(e) => setSelected(e.target.checked ? [...ROOMS] : [])}
/>
{ROOMS.map((room) => (
<Checkbox
key={room}
label={room}
tabIndex={0}
isChecked={selected.includes(room)}
onChange={(e) => toggle(room, e.target.checked)}
/>
))}
</div>
);
}
Behaviour the types don't state
- It is not a controlled component. It keeps its own
checkedstate, seeded fromisCheckedand re-seeded whenever that prop changes. A click flips the box immediately, and if your handler rejects the change the box stays flipped untilisCheckedmoves to a new value — passing the same value back does not reset it, because the component only reacts to a change. - The focusable element is the icon, not the input. The
<input>beneath is hard-coded totabIndex={-1}, andtabIndexgoes on the SVG instead. It defaults to0, so the control is reachable without anything being passed; it used to default to-1, which took every checkbox out of the tab order. - The change event does not bubble. The component calls
stopPropagation(), so a form listening forchangeat its root never sees it; read the state throughonChange. - The label is in the flow beside the box and wraps by default.
truncateclips it to one line with an ellipsis instead. helpButtonrenders after the label, and a click on it is swallowed — the checkbox does not toggle.titleis read by the<label>that wraps the whole control, so the shared tooltip opens when the pointer rests anywhere on the box or the label — and only onceRootTooltipis mounted.- Anything else you pass is spread onto the input element, not onto the label the other props reach.
CSS variables
The other colour variables (--checkbox-fill-color-default, --checkbox-error-color and the
rest) are not consumer settings: the stylesheet assigns them on the component's own
element, so a value set on an ancestor is overridden. Theme them through the theme provider
instead. A disabled box ignores all three colour settings above and draws its own disabled
colours.
Accessibility
- The control is a real
<input type="checkbox">wrapped in a<label>, so a click on the label toggles it and a screen reader announces a checkbox with its state and its label. - The input is hidden from the tab order (
tabIndex={-1}, always). Keyboard focus lands on the icon instead (tabIndex,0by default), and the focused box draws its border in the focus colour. Space and Enter on the focused icon do not toggle it — only a click does. isDisabledsets the nativedisabledon the input, which is announced as unavailable.isIndeterminatesets the input's DOMindeterminateproperty, so the mixed state is announced, not merely drawn.- Without
labelthe control has no accessible name and no prop supplies one.
Test ids
| Element | data-testid |
|---|---|
The <label> wrapper | checkbox, overridable with dataTestId |
| The help button slot | checkbox-help-button |
Related
ToggleButton— for a setting that applies immediately.RadioButtonGroup— for one choice out of several.FieldContainer— for the field label and the error message this component'shasErroronly colours.