Checkbox
Checkboxes provide a straightforward way for users to make multiple selections from a set of options, whether they're choosing preferences, confirming actions, or filtering content.
import {Checkbox} from "@qualcomm-ui/react/checkbox"Examples
Simple
The simple API provides a standalone component with built-in layout.
<Checkbox label="Checkbox" />
Composite
Build with the composite API for granular control. This API requires you to provide each subcomponent, but gives you full control over the structure and layout.
<Checkbox.Root>
<Checkbox.HiddenInput />
<Checkbox.Label>Label</Checkbox.Label>
<Checkbox.Control />
</Checkbox.Root>
States
Based on the inputs, the checkbox will render as checked, indeterminate, or unchecked (default). The checked state takes precedence over indeterminate.
| checked | indeterminate | Result State |
|---|---|---|
false | false | |
false | true | |
true | false | |
true | true |
<Checkbox defaultChecked label="Label" />
<Checkbox label="Label" />
<Checkbox indeterminate label="Label" />
Disabled State
When disabled is true, the checkbox becomes non-interactive and is typically rendered with reduced opacity to indicate its unavailable state.
<Checkbox defaultChecked disabled label="Label" />
<Checkbox disabled label="Label" />
<Checkbox disabled indeterminate label="Label" />
Controlled State
Set the initial value using the defaultChecked prop, or use checked and onCheckedChange to control the value manually. These props follow our controlled state pattern.
import {type ReactElement, useState} from "react"
import {Checkbox} from "@qualcomm-ui/react/checkbox"
export function CheckboxControlledDemo(): ReactElement {
const [checked, setChecked] = useState<boolean>(false)
return (
<Checkbox.Root
checked={checked}
onCheckedChange={(nextState) => {
console.log("checkbox state change:", nextState)
setChecked(nextState)
}}
>
<Checkbox.HiddenInput />
<Checkbox.Control />
<Checkbox.Label>Label</Checkbox.Label>
</Checkbox.Root>
)
}Sizes
Checkboxes come in three sizes: sm, md, and lg. The default size is md.
import type {ReactElement} from "react"
import {Checkbox} from "@qualcomm-ui/react/checkbox"
export function CheckboxSizesDemo(): ReactElement {
return (
<div className="flex flex-col items-start gap-4">
<Checkbox label="Small (sm)" size="sm" />
<Checkbox label="Medium (md)" size="md" />
<Checkbox label="Large (lg)" size="lg" />
</div>
)
}Hint
Add a hint to provide additional context below the checkbox.
<Checkbox hint="Keep me signed in for 30 days" label="Remember me" />
Group
Use CheckboxGroup to group related checkboxes with a shared label, hint text, and validation state.
Forms
Choose the form library that fits your needs—we've built examples with React Hook Form and Tanstack Form to get you started.
React Hook Form
Use React Hook Form to handle the checkbox state and validation. ArkType works great for schema validation if you need it.
import type {ReactElement} from "react"
import {arktypeResolver} from "@hookform/resolvers/arktype"
import {type} from "arktype"
import {Controller, useForm} from "react-hook-form"
import {Button} from "@qualcomm-ui/react/button"
import {Checkbox} from "@qualcomm-ui/react/checkbox"
interface FormData {
acceptTerms: boolean
newsletter: boolean
}
const acceptTermsSchema = type("boolean")
.narrow((value: boolean) => value === true)
.configure({
message: "Please accept the Terms of Service to continue",
})
const FormSchema = type({
// must be true
acceptTerms: acceptTermsSchema,
// can be true or false
newsletter: "boolean",
})
export function CheckboxReactHookFormDemo(): ReactElement {
const {control, handleSubmit} = useForm<FormData>({
defaultValues: {
acceptTerms: false,
newsletter: true,
},
resolver: arktypeResolver(FormSchema),
})
return (
<form
className="flex w-56 flex-col gap-2"
onSubmit={(e) => {
void handleSubmit((data) => console.log(data))(e)
}}
>
<Controller
control={control}
name="newsletter"
render={({field: {name, onChange, value, ...fieldProps}}) => {
return (
<Checkbox.Root
checked={value}
name={name}
onCheckedChange={onChange}
{...fieldProps}
>
<Checkbox.HiddenInput />
<Checkbox.Control />
<Checkbox.Label>Subscribe to our Newsletter</Checkbox.Label>
</Checkbox.Root>
)
}}
/>
<Controller
control={control}
name="acceptTerms"
render={({
field: {onChange, value, ...fieldProps},
fieldState: {error},
}) => {
return (
<Checkbox.Root
checked={value}
invalid={!!error?.message}
onCheckedChange={onChange}
{...fieldProps}
>
<Checkbox.HiddenInput />
<Checkbox.Control />
<Checkbox.Label>Accept Terms of Service</Checkbox.Label>
<Checkbox.ErrorText>{error?.message}</Checkbox.ErrorText>
</Checkbox.Root>
)
}}
/>
<Button
className="mt-1"
emphasis="primary"
size="sm"
type="submit"
variant="fill"
>
Submit
</Button>
</form>
)
}Tanstack Form
Tanstack Form handles checkbox validation with its built-in validators.
import type {ReactElement} from "react"
import {useForm} from "@tanstack/react-form"
import {Button} from "@qualcomm-ui/react/button"
import {Checkbox} from "@qualcomm-ui/react/checkbox"
interface FormData {
acceptTerms: boolean
newsletter: boolean
}
const defaultFormData: FormData = {
acceptTerms: false,
newsletter: true,
}
const errorMessage = "Please accept the Terms of Service to continue"
export function CheckboxTanstackFormDemo(): ReactElement {
const form = useForm({
defaultValues: defaultFormData,
onSubmit: ({value}) => {
// Do something with form data
console.log(value)
},
})
return (
<form
className="flex w-56 flex-col gap-2"
onSubmit={(event) => {
event.preventDefault()
event.stopPropagation()
void form.handleSubmit()
}}
>
<form.Field name="newsletter">
{({handleChange, name, state}) => {
return (
<Checkbox
checked={state.value}
label="Subscribe to our Newsletter"
name={name}
onCheckedChange={handleChange}
/>
)
}}
</form.Field>
<form.Field
name="acceptTerms"
validators={{
onChange: (field) => (field.value ? undefined : errorMessage),
onSubmit: (field) => (field.value ? undefined : errorMessage),
}}
>
{({handleChange, name, state}) => {
const fieldError =
state.meta.errorMap["onChange"] || state.meta.errorMap["onSubmit"]
return (
<Checkbox
checked={state.value}
errorText={fieldError}
invalid={!!fieldError}
label="Accept Terms"
name={name}
onCheckedChange={handleChange}
/>
)
}}
</form.Field>
<Button
className="mt-1"
emphasis="primary"
size="sm"
type="submit"
variant="fill"
>
Submit
</Button>
</form>
)
}Tanstack form also supports ArkType.
Composite Guidelines
The composite elements are only intended to be used as direct descendants of the <Checkbox.Root> component.
/* Won't work alone ❌ */
<Checkbox.HiddenInput />
<Checkbox.Control />
/* Works as expected ✅ */
<Checkbox.Root>
<Checkbox.HiddenInput />
<Checkbox.Control />
</Checkbox.Root>Explorer
API
<Checkbox>
The Checkbox extends the Checkbox.Root with the following props:
stringstringComposite API
<Checkbox.Root>
booleanboolean'ltr' | 'rtl'
booleanstring() =>
| Node
| ShadowRoot
| Document
stringPartial<{
control: string
errorText: string
hiddenInput: string
hint: string
label: string
root: string
}>
booleanindeterminate state.booleanstring(
checked: boolean,
) => void
(
focused: boolean,
) => void
boolean| ReactElement
| ((
props: object,
) => ReactElement)
boolean| 'sm'
| 'md'
| 'lg'
stringclassName'qui-checkbox__root'data-activedata-checkbox-part'root'data-disableddata-focusdata-focus-visibledata-hoverdata-invaliddata-readonlydata-state| 'checked'
| 'indeterminate'
| 'unchecked'
<Checkbox.Label>
<span> element by default.string| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-checkbox__label'data-activedata-checkbox-part'label'data-disableddata-focusdata-focus-visibledata-hoverdata-invaliddata-readonlydata-size| 'sm'
| 'md'
| 'lg'
data-state| 'checked'
| 'indeterminate'
| 'unchecked'
<Checkbox.Control>
<div> element by
default.string| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-checkbox__control'data-activedata-checkbox-part'control'data-disableddata-focusdata-focus-visibledata-hoverdata-invaliddata-readonlydata-size| 'sm'
| 'md'
| 'lg'
data-state| 'checked'
| 'indeterminate'
| 'unchecked'
<Checkbox.ErrorText>
<div> element
by default.| LucideIcon
| ReactNode
string| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-input__error-text'data-activedata-checkbox-part'error-text'data-disableddata-focusdata-focus-visibledata-hoverdata-invaliddata-readonlydata-state| 'checked'
| 'indeterminate'
| 'unchecked'
hiddenboolean<Checkbox.Hint>
<div> element by
default.string| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-input__hint'data-activedata-checkbox-part'hint'data-disableddata-focusdata-focus-visibledata-hoverdata-invaliddata-readonlydata-state| 'checked'
| 'indeterminate'
| 'unchecked'
hiddenboolean<Checkbox.HiddenInput>
<input> element. Note: do not apply typical input props like disabled
to this element. Those are applied to the root element and propagated via
internal context.stringclassName'qui-checkbox__hidden-input'data-activedata-checkbox-part'hidden-input'data-disableddata-focusdata-focus-visibledata-hoverdata-invaliddata-readonlydata-state| 'checked'
| 'indeterminate'
| 'unchecked'
style<Checkbox.Indicator>
<div> element by
default.| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-checkbox__indicator'data-activedata-checkbox-part'indicator'data-disableddata-focusdata-focus-visibledata-hoverdata-invaliddata-readonlydata-size| 'sm'
| 'md'
| 'lg'
data-state| 'checked'
| 'indeterminate'
| 'unchecked'
hiddenboolean
<label>element by default.