Source | Original component
A single checkbox. It has no text label of its own — when you need one, wrap it in FieldLayout.
Basic usage
The checked state uses the checked / defaultChecked / onChange channels; the first argument of onChange is the new checked boolean.
import { useState } from "react";
import { CheckboxInput } from "ooui-react";
function App() {
const [checked, setChecked] = useState(false);
return (
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
<label style={{ display: "flex", alignItems: "center", gap: 8 }}>
<CheckboxInput checked={checked} onChange={setChecked} />
Checked: {String(checked)}
</label>
<label style={{ display: "flex", alignItems: "center", gap: 8 }}>
<CheckboxInput defaultChecked />
Uncontrolled; initially checked
</label>
</div>
);
}
export default App;
Indeterminate state
indeterminate means "partially selected" — visually a dash rather than a check. It is independent of the checked state and is commonly used on a "select all" box to show that some, not all, children are selected.
import { CheckboxInput } from "ooui-react";
function App() {
return (
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
<CheckboxInput indeterminate />
<CheckboxInput checked indeterminate />
</div>
);
}
export default App;
The standard wiring of a "select all" box: its checked and indeterminate states are both derived from the children set (none → unchecked, all → checked, otherwise indeterminate), and clicking it sets all children to checked or unchecked:
import { useState } from "react";
import { CheckboxInput } from "ooui-react";
const ITEMS = ["Apple", "Banana", "Orange"];
function App() {
const [picked, setPicked] = useState<string[]>(["Apple"]);
const allChecked = picked.length === ITEMS.length;
return (
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
<label style={{ display: "flex", alignItems: "center", gap: 8 }}>
<CheckboxInput
checked={allChecked}
indeterminate={picked.length > 0 && !allChecked}
onChange={() => setPicked(allChecked ? [] : ITEMS)}
/>
Select all
</label>
{ITEMS.map((item) => (
<label key={item} style={{ display: "flex", alignItems: "center", gap: 8 }}>
<CheckboxInput
checked={picked.includes(item)}
onChange={() =>
setPicked((prev) =>
prev.includes(item) ? prev.filter((x) => x !== item) : [...prev, item],
)
}
/>
{item}
</label>
))}
</div>
);
}
export default App;
value is the form submission value (written to the native <input>'s value attribute, defaulting to an empty string). It does not affect the checked state — that is driven only by the checked channels. Combined with name, this value is submitted with the form when checked.
import { CheckboxInput } from "ooui-react";
function App() {
return (
<label style={{ display: "flex", alignItems: "center", gap: 8 }}>
<CheckboxInput name="agree" value="yes" defaultChecked />
I agree to the terms (submits as agree=yes)
</label>
);
}
export default App;
Using it with FieldLayout
The field's text label is usually delegated to FieldLayout. It automatically associates the label with this checkbox's native <input> (clicking the label toggles the check); to specify the association target manually, give the input element's id via inputId.
import { CheckboxInput, FieldLayout } from "ooui-react";
function App() {
return (
<div style={{ maxWidth: 320 }}>
<FieldLayout label="Enable notifications" align="inline">
<CheckboxInput defaultChecked />
</FieldLayout>
</div>
);
}
export default App;
Disabled
disabled outputs aria-disabled and disables the native <input>, so the checkbox can no longer be toggled. Inside a native <fieldset> such as FieldsetLayout, the group-level disable propagates to it.
import { CheckboxInput } from "ooui-react";
function App() {
return (
<div style={{ display: "flex", alignItems: "center", gap: 16 }}>
<CheckboxInput disabled />
<CheckboxInput disabled defaultChecked />
</div>
);
}
export default App;
API
title, dir and tabIndex are taken over by the component and land on the inner <input>.
See also