Checkbox Group
A group of checkboxes that can be controlled together.
Pick a genre
export default function CheckboxGroupHero() { return ( <div className="flex flex-col gap-3"> <p className="text-sm font-medium text-foreground">Pick a genre</p> <CheckboxGroup defaultValue={['jazz']}> {GENRES.map((genre) => ( <label key={genre.value} className="flex items-center gap-2"> <Checkbox value={genre.value} /> <span className="text-sm text-foreground">{genre.label}</span> </label> ))} </CheckboxGroup> </div> ); }
Installation
pnpm dlx shadcn@latest add "https://ora-ui.com/r/checkbox-group.json"
Usage
import { Checkbox } from '@/registry/ui/checkbox';
import { CheckboxGroup } from '@/registry/ui/checkbox-group';
<CheckboxGroup defaultValue={['jazz']}>
<label className="flex items-center gap-2">
<Checkbox value="jazz" />
<span>Jazz</span>
</label>
<label className="flex items-center gap-2">
<Checkbox value="electronic" />
<span>Electronic</span>
</label>
</CheckboxGroup>;Examples
Default
A labeled group of checkboxes with a default selection.
Sound preferences
export default function CheckboxGroupDefault() { return ( <div className="flex flex-col gap-3"> <p className="text-sm font-medium text-foreground">Sound preferences</p> <CheckboxGroup defaultValue={['acoustic']}> {PREFERENCES.map((pref) => ( <label key={pref.value} className="flex items-center gap-2"> <Checkbox value={pref.value} /> <span className="text-sm text-foreground">{pref.label}</span> </label> ))} </CheckboxGroup> </div> ); }
Variant
Two visual styles are available: solid fills the checkbox on check, surface uses a border treatment. Set variant on CheckboxGroup to apply it to all checkboxes in the group.
Pick a genre
export function CheckboxGroupSolid() { return ( <div className="flex flex-col gap-3"> <p className="text-sm font-medium text-foreground">Pick a genre</p> <CheckboxGroup variant="solid" defaultValue={['jazz']}> {GENRES.map((genre) => ( <label key={genre.value} className="flex items-center gap-2"> <Checkbox value={genre.value} /> <span className="text-sm text-foreground">{genre.label}</span> </label> ))} </CheckboxGroup> </div> ); }
Theme
Use the theme prop to apply a color theme to the group. gray is the default; accent uses the brand color.
Pick a genre
export function CheckboxGroupGray() { return ( <div className="flex flex-col gap-3"> <p className="text-sm font-medium text-foreground">Pick a genre</p> <CheckboxGroup theme="gray" defaultValue={['jazz']}> {GENRES.map((genre) => ( <label key={genre.value} className="flex items-center gap-2"> <Checkbox value={genre.value} /> <span className="text-sm text-foreground">{genre.label}</span> </label> ))} </CheckboxGroup> </div> ); }
Parent checkbox
Use a parent Checkbox with the parent prop alongside allValues to create a "select all" control that reflects indeterminate state when only some items are checked.
export default function CheckboxGroupParent() { const [values, setValues] = React.useState<string[]>(['jazz']); const allChecked = values.length === GENRES.length; const someChecked = values.length > 0 && !allChecked; return ( <CheckboxGroup value={values} onValueChange={setValues} allValues={GENRES.map((g) => g.value)} className="gap-3" > <label className="flex items-center gap-2"> <Checkbox parent checked={allChecked} indeterminate={someChecked} onCheckedChange={() => setValues(allChecked ? [] : GENRES.map((g) => g.value))} /> <span className="text-sm font-medium text-foreground">All genres</span> </label> <div className="ml-6 flex flex-col gap-2 border-l border-line-subtle pl-4"> {GENRES.map((genre) => ( <label key={genre.value} className="flex items-center gap-2"> <Checkbox value={genre.value} /> <span className="text-sm text-foreground">{genre.label}</span> </label> ))} </div> </CheckboxGroup> ); }
Nested parent checkbox
Combine a top-level parent with nested category parents to build multi-level selection trees.
export default function CheckboxGroupNested() { const [values, setValues] = React.useState<string[]>(['house', 'pop']); const allChecked = values.length === ALL_VALUES.length; const someChecked = values.length > 0 && !allChecked; const allElectronicChecked = ELECTRONIC.every((i) => values.includes(i.value)); const someElectronicChecked = ELECTRONIC.some((i) => values.includes(i.value)) && !allElectronicChecked; return ( <CheckboxGroup value={values} onValueChange={setValues} allValues={ALL_VALUES} className="gap-3" > <label className="flex items-center gap-2"> <Checkbox parent checked={allChecked} indeterminate={someChecked} onCheckedChange={() => setValues(allChecked ? [] : ALL_VALUES)} /> <span className="text-sm font-medium text-foreground">All genres</span> </label> <div className="ml-6 flex flex-col gap-2 border-l border-line-subtle pl-4"> <label className="flex items-center gap-2"> <Checkbox value="pop" /> <span className="text-sm text-foreground">Pop</span> </label> <label className="flex items-center gap-2"> <Checkbox value="jazz" /> <span className="text-sm text-foreground">Jazz</span> </label> <div className="flex flex-col gap-2"> <label className="flex items-center gap-2"> <Checkbox checked={allElectronicChecked} indeterminate={someElectronicChecked} onCheckedChange={() => setValues((prev) => allElectronicChecked ? prev.filter((v) => !ELECTRONIC.map((i) => i.value).includes(v)) : [...new Set([...prev, ...ELECTRONIC.map((i) => i.value)])] ) } /> <span className="text-sm font-medium text-foreground">Electronic</span> </label> <div className="ml-6 flex flex-col gap-2 border-l border-line-subtle pl-4"> {ELECTRONIC.map((item) => ( <label key={item.value} className="flex items-center gap-2"> <Checkbox value={item.value} /> <span className="text-sm text-foreground">{item.label}</span> </label> ))} </div> </div> </div> </CheckboxGroup> ); }
API Reference
CheckboxGroup
| Prop | Type | Default |
|---|---|---|
variant | "solid" | "surface" | "solid" |
theme | "gray" | "accent" | "gray" |
All other props are forwarded to the underlying Base UI CheckboxGroup primitive.