@xsolla/xui-checkbox
v0.175.0
Published
A cross-platform React checkbox component with label, description, indeterminate state, and validation support. Works on both React (web) and React Native. <!-- BEGIN:xui-mcp-instructions:checkbox --> Use for multiple selection of options from a list, for
Readme
Checkbox
A cross-platform React checkbox component with label, description, indeterminate state, and validation support. Works on both React (web) and React Native.
Use for multiple selection of options from a list, for agreements with policies, and sometimes for enabling parameters. The Checkbox could be embedded inside other components.
Do not use when a user can choose only one option from a list — use a Radio instead.
Checkbox vs ToggleButtonGroup:
- Decision guide
When to use
- When the user can select multiple options independently from a list
- For agreements and consent flows (e.g. "I agree to the Terms of Service")
- For enabling or disabling individual parameters in a settings list
- When the selection does not take immediate effect and is submitted as part of a form
- Inside other components: table cells, ContextMenu items, list rows, and similar containers
When not to use
- When only one option can be selected — use a Radio group
- When the change takes immediate effect — use a Switch
- When there are only two mutually exclusive states — use a Switch
Content guidelines
- Label should describe the specific option being selected, written in sentence case: "Send email notifications", "I agree to the Terms of Service".
- Keep labels short — one line where possible. If more context is needed, use Description.
- Description should clarify the consequence or meaning of selecting the option. Keep it to one sentence.
- Error messages should be specific: "You must agree to continue" — not just "Required".
Behaviour guidelines
- Checkboxes in a list are independent — selecting one does not affect others unless an explicit parent/child relationship is defined.
- Unlike a Switch, a Checkbox typically requires a form submission to apply the selection. Do not trigger side effects immediately on check.
- When using a parent/child group with Indeterminate, ensure the parent*'s state always reflects the children'*s combined state in real time.
- Disabled checkboxes preserve their current value visually. Provide a tooltip explaining why the checkbox is disabled.
- In tables, clicking a row should not automatically check its checkbox — these are separate interactions unless explicitly designed otherwise.
Accessibility
- Use role="checkbox" with aria-checked="true", aria-checked="false", or aria-checked="mixed" for the Indeterminate state
- Always associate a visible label with the checkbox via htmlFor / id
- Description text should be linked via aria-describedby so screen readers announce it alongside the label
- Error message must be linked via aria-describedby so it is announced on focus in the Error state
- Disabled checkboxes should use aria-disabled="true" rather than the HTML disabled attribute to remain focusable
- The minimum touch target is 44×44px. Sizes S (16px) and M (18px) require additional padding in touch contexts
- The component does not define a focus ring visually — this must be implemented in code
Installation
npm install @xsolla/xui-checkboxDemo
Basic Checkbox
import * as React from "react";
import { Checkbox } from "@xsolla/xui-checkbox";
export default function BasicCheckbox() {
const [checked, setChecked] = React.useState(false);
return (
<Checkbox checked={checked} onChange={(e) => setChecked(e.target.checked)}>
Accept terms and conditions
</Checkbox>
);
}Checkbox with Description
import * as React from "react";
import { Checkbox } from "@xsolla/xui-checkbox";
export default function CheckboxWithDescription() {
const [checked, setChecked] = React.useState(false);
return (
<Checkbox
checked={checked}
onChange={(e) => setChecked(e.target.checked)}
description="You will receive notifications about updates and promotions"
>
Subscribe to newsletter
</Checkbox>
);
}Checkbox Sizes
import * as React from "react";
import { Checkbox } from "@xsolla/xui-checkbox";
export default function CheckboxSizes() {
return (
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
<Checkbox size="sm">Small checkbox</Checkbox>
<Checkbox size="md">Medium checkbox (default)</Checkbox>
<Checkbox size="lg">Large checkbox</Checkbox>
<Checkbox size="xl">Extra large checkbox</Checkbox>
</div>
);
}Indeterminate State
import * as React from "react";
import { Checkbox } from "@xsolla/xui-checkbox";
export default function IndeterminateCheckbox() {
const [items, setItems] = React.useState([
{ id: 1, label: "Item 1", checked: true },
{ id: 2, label: "Item 2", checked: false },
{ id: 3, label: "Item 3", checked: true },
]);
const allChecked = items.every((item) => item.checked);
const someChecked = items.some((item) => item.checked);
const handleSelectAll = () => {
setItems(items.map((item) => ({ ...item, checked: !allChecked })));
};
return (
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
<Checkbox
checked={allChecked}
indeterminate={someChecked && !allChecked}
onChange={handleSelectAll}
>
Select all
</Checkbox>
<div
style={{
marginLeft: 24,
display: "flex",
flexDirection: "column",
gap: 8,
}}
>
{items.map((item) => (
<Checkbox
key={item.id}
checked={item.checked}
onChange={(e) => {
setItems(
items.map((i) =>
i.id === item.id ? { ...i, checked: e.target.checked } : i
)
);
}}
>
{item.label}
</Checkbox>
))}
</div>
</div>
);
}Anatomy
Import the component and use it directly:
import { Checkbox } from "@xsolla/xui-checkbox";
<Checkbox
checked={checked} // Controlled checked state
onChange={handleChange} // Change handler
indeterminate={false} // Indeterminate/mixed state
size="md" // Size variant
description="Help text" // Description below label
errorMessage="Error text" // Error message
disabled={false} // Disabled state
>
Label text
</Checkbox>;Examples
Error State
import * as React from "react";
import { Checkbox } from "@xsolla/xui-checkbox";
export default function ErrorCheckbox() {
return (
<Checkbox
checked={false}
errorMessage="You must accept the terms to continue"
>
I accept the terms and conditions
</Checkbox>
);
}Disabled Checkbox
import * as React from "react";
import { Checkbox } from "@xsolla/xui-checkbox";
export default function DisabledCheckbox() {
return (
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
<Checkbox disabled>Disabled unchecked</Checkbox>
<Checkbox disabled checked>
Disabled checked
</Checkbox>
<Checkbox disabled indeterminate>
Disabled indeterminate
</Checkbox>
</div>
);
}Form Integration
import * as React from "react";
import { Checkbox } from "@xsolla/xui-checkbox";
import { Button } from "@xsolla/xui-button";
export default function FormCheckbox() {
const [preferences, setPreferences] = React.useState({
marketing: false,
updates: true,
newsletter: false,
});
const handleChange =
(key: string) => (e: { target: { checked: boolean } }) => {
setPreferences((prev) => ({ ...prev, [key]: e.target.checked }));
};
return (
<form
onSubmit={(e) => {
e.preventDefault();
console.log(preferences);
}}
>
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
<Checkbox
name="marketing"
checked={preferences.marketing}
onChange={handleChange("marketing")}
>
Receive marketing emails
</Checkbox>
<Checkbox
name="updates"
checked={preferences.updates}
onChange={handleChange("updates")}
>
Receive product updates
</Checkbox>
<Checkbox
name="newsletter"
checked={preferences.newsletter}
onChange={handleChange("newsletter")}
>
Subscribe to newsletter
</Checkbox>
<Button type="submit">Save Preferences</Button>
</div>
</form>
);
}API Reference
Checkbox
The main checkbox component with label support.
Checkbox Props:
| Prop | Type | Default | Description |
| :------------ | :----------------------------------------------------------------------------- | :------ | :------------------------------------------------------------------------------------------------------------ |
| testID | string | — | Test ID for testing frameworks. On web this renders as data-testid; on React Native it renders as testID. |
| children | ReactNode | - | Label content displayed next to the checkbox. |
| checked | boolean | false | Whether the checkbox is checked. |
| indeterminate | boolean | false | Whether to show indeterminate (mixed) state. |
| size | "sm" \| "md" \| "lg" \| "xl" | "md" | Size of the checkbox. |
| disabled | boolean | false | Whether the checkbox is disabled. |
| description | string | - | Description text displayed below the label. |
| errorMessage | string | - | Error message displayed below (shows error styling). |
| error | boolean | false | Show error styling without message. |
| name | string | - | HTML name attribute for form submission. |
| value | string | - | HTML value attribute for form submission. |
| onChange | (e: { target: { checked: boolean; name?: string; value?: string } }) => void | - | Change event handler. |
| id | string | - | HTML id attribute. |
| aria-label | string | - | Accessible label for screen readers. |
Checkbox Ref Methods:
| Method | Description |
| :-------- | :----------------------------------- |
| focus() | Programmatically focus the checkbox. |
| blur() | Programmatically blur the checkbox. |
Size Configuration:
| Size | Checkbox | Icon | Gap | Label font/line | Description font/line | | :--- | :------- | :--- | :--- | :-------------- | :-------------------- | | sm | 16px | 10px | 6px | 14 / 16 | 12 / 14 | | md | 18px | 12px | 8px | 16 / 18 | 14 / 16 | | lg | 20px | 14px | 10px | 18 / 20 | 16 / 18 | | xl | 24px | 16px | 12px | 20 / 22 | 18 / 20 |
Frame border is 1px with 4px radius at every size.
Accessibility
role="checkbox"witharia-checkedreflecting state ("true","false","mixed").- Keyboard: Tab to focus, Space or Enter to toggle.
- Visible focus ring: 2px solid
border.brandoutline with 2px offset. aria-invalidset whenerrororerrorMessageis supplied.aria-describedbylinks the label to description and error nodes when present.aria-labelledbylinks the control to its visible label;aria-labelis used when there is no visible label.- Disabled checkboxes use
aria-disabled="true"and are removed from the tab order.
