Checkbox
React formStandalone or in-form binary toggle, built on Radix UI. Supports indeterminate state for tri-state lists, sizes, and proper keyboard / screen-reader behavior. Pair with Label for clickable text.
Also available for Vue ->Installation
$ pnpm dlx shadcn@latest add https://react.uipkge.dev/r/react/checkbox.json$ npx shadcn@latest add https://react.uipkge.dev/r/react/checkbox.json$ yarn dlx shadcn@latest add https://react.uipkge.dev/r/react/checkbox.json$ bunx shadcn@latest add https://react.uipkge.dev/r/react/checkbox.json
Or with the named registry:
npx shadcn@latest add @uipkge-react/checkbox
Examples
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
checked The controlled checked state of the checkbox. | boolean | 'indeterminate' | — | optional |
defaultChecked The default checked state when uncontrolled. | boolean | 'indeterminate' | — | optional |
value The value given as data when submitted with a name. | string | — | optional |
id Id of the element | string | — | optional |
size Size of the checkbox | 'sm' | 'md' | 'lg' | — | optional |
color Custom color for the checked state - matches Vuetify color system | 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info' | string | — | optional |
label Label text displayed next to the checkbox | string | — | optional |
hint Hint text shown below the checkbox | string | — | optional |
errorMessages Error message to display | string | string[] | — | optional |
error Whether to show error state | boolean | — | optional |
disabled When `true`, prevents the user from interacting with the checkbox | boolean | — | optional |
indeterminate When `true`, shows an indeterminate state | boolean | — | optional |
density Density of the checkbox - affects spacing | 'compact' | 'default' | 'comfortable' | — | optional |
hideIcon When `true`, the icon is hidden | boolean | — | optional |
loading Loading state - shows a spinner | boolean | — | optional |
labelPosition Label position - before or after the checkbox | 'before' | 'after' | — | optional |
flat Whether the checkbox appears flat (no elevation) | boolean | — | optional |
inline Inline text style | boolean | — | optional |
name Name attribute for form submission | string | — | optional |
Schema
Type aliases from this item's source — use them to shape the data you pass in.
CheckboxOption interface CheckboxOption {
label: string
value: string
disabled?: boolean
} Dependencies
Used by
Files (2)
-
components/ui/checkbox/checkbox.tsx 11.9 kB
'use client' import * as React from 'react' import * as CheckboxPrimitive from '@radix-ui/react-checkbox' import { Check, Minus, Loader2 } from 'lucide-react' import { cn } from '@/lib/utils' export interface CheckboxProps extends Omit<React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>, 'checked' | 'defaultChecked'> { /** The controlled checked state of the checkbox. */ checked?: boolean | 'indeterminate' /** The default checked state when uncontrolled. */ defaultChecked?: boolean | 'indeterminate' /** The value given as data when submitted with a name. */ value?: string /** Id of the element */ id?: string /** Size of the checkbox */ size?: 'sm' | 'md' | 'lg' /** Custom color for the checked state - matches Vuetify color system */ color?: 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info' | string /** Label text displayed next to the checkbox */ label?: string /** Hint text shown below the checkbox */ hint?: string /** Error message to display */ errorMessages?: string | string[] /** Whether to show error state */ error?: boolean /** When `true`, prevents the user from interacting with the checkbox */ disabled?: boolean /** When `true`, shows an indeterminate state */ indeterminate?: boolean /** Density of the checkbox - affects spacing */ density?: 'compact' | 'default' | 'comfortable' /** When `true`, the icon is hidden */ hideIcon?: boolean /** Loading state - shows a spinner */ loading?: boolean /** Label position - before or after the checkbox */ labelPosition?: 'before' | 'after' /** Whether the checkbox appears flat (no elevation) */ flat?: boolean /** Inline text style */ inline?: boolean /** Name attribute for form submission */ name?: string } // Size classes const sizeClasses = { sm: 'size-3.5', md: 'size-4', lg: 'size-5', } const iconSizes = { sm: 'size-2.5', md: 'size-3.5', lg: 'size-4', } // Color classes for checked state const colorClasses: Record<string, string> = { primary: 'data-[state=checked]:bg-primary data-[state=checked]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:border-primary', secondary: 'data-[state=checked]:bg-secondary data-[state=checked]:border-secondary data-[state=indeterminate]:bg-secondary data-[state=indeterminate]:border-secondary', success: 'data-[state=checked]:bg-[var(--success)] data-[state=checked]:border-[var(--success)] data-[state=indeterminate]:bg-[var(--success)] data-[state=indeterminate]:border-[var(--success)]', warning: 'data-[state=checked]:bg-[var(--warning)] data-[state=checked]:border-[var(--warning)] data-[state=indeterminate]:bg-[var(--warning)] data-[state=indeterminate]:border-[var(--warning)]', error: 'data-[state=checked]:bg-destructive data-[state=checked]:border-destructive data-[state=indeterminate]:bg-destructive data-[state=indeterminate]:border-destructive', info: 'data-[state=checked]:bg-[var(--info)] data-[state=checked]:border-[var(--info)] data-[state=indeterminate]:bg-[var(--info)] data-[state=indeterminate]:border-[var(--info)]', } // Density classes const densityClasses = { compact: 'gap-1', default: 'gap-2', comfortable: 'gap-3', } const Checkbox = React.forwardRef<React.ElementRef<typeof CheckboxPrimitive.Root>, CheckboxProps>( ( { className, checked, defaultChecked, value, id, size = 'md', color = 'primary', label, hint, errorMessages, error, disabled, indeterminate, density = 'default', hideIcon = false, loading, labelPosition = 'after', flat = false, inline = false, name, children, ...props }, ref, ) => { const hasError = Boolean(error) || Boolean(errorMessages && (typeof errorMessages === 'string' ? errorMessages : errorMessages.length > 0)) // Determine if the checkbox is in an indeterminate state const isIndeterminate = Boolean(indeterminate) || checked === 'indeterminate' const resolvedChecked: boolean | 'indeterminate' | undefined = isIndeterminate ? 'indeterminate' : checked const checkboxClasses = cn( 'peer border-input data-[state=checked]:text-primary-foreground data-[state=indeterminate]:text-primary-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive shrink-0 rounded-[4px] border shadow-xs transition-colors duration-200 outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50', sizeClasses[size], colorClasses[color] || colorClasses.primary, hasError && 'border-destructive data-[state=checked]:!bg-destructive data-[state=checked]:!border-destructive data-[state=indeterminate]:!bg-destructive data-[state=indeterminate]:!border-destructive', flat && 'shadow-none', className, ) const labelClasses = cn( 'cursor-pointer text-sm leading-none font-medium select-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70', hasError ? 'text-destructive' : '', disabled && 'cursor-not-allowed opacity-50', ) return ( <div className={cn('flex items-start', densityClasses[density], inline ? 'inline-flex' : 'flex-col')}> {label && labelPosition === 'before' && ( <label htmlFor={id} className={cn('mr-2', labelClasses)}> {label} </label> )} <div className="flex items-center"> <CheckboxPrimitive.Root ref={ref} data-slot="checkbox" id={id} value={value} name={name} disabled={disabled} checked={resolvedChecked} defaultChecked={defaultChecked} className={checkboxClasses} {...props} > <CheckboxPrimitive.Indicator data-uipkge="" data-slot="checkbox-indicator" forceMount={isIndeterminate || hideIcon ? true : undefined} className="grid place-content-center text-current transition-none" > {children ?? ( <> {loading ? ( <Loader2 className={cn(iconSizes[size], 'animate-spin')} /> ) : isIndeterminate ? ( <Minus className={iconSizes[size]} /> ) : !hideIcon ? ( <Check className={iconSizes[size]} /> ) : null} </> )} </CheckboxPrimitive.Indicator> </CheckboxPrimitive.Root> {label && labelPosition === 'after' && ( <label htmlFor={id} className={cn('ml-2', labelClasses)}> {label} </label> )} </div> {hint && !hasError && <p className="text-muted-foreground mt-1 text-xs">{hint}</p>} {hasError && ( <div className="mt-1 flex flex-col gap-0.5"> {typeof errorMessages === 'string' ? ( <p className="text-destructive text-xs">{errorMessages}</p> ) : ( errorMessages?.map((msg, i) => ( <p key={i} className="text-destructive text-xs"> {msg} </p> )) )} </div> )} </div> ) }, ) Checkbox.displayName = 'Checkbox' export interface CheckboxOption { label: string value: string disabled?: boolean } export interface CheckboxGroupProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'defaultValue'> { /** The value of the checkbox items that should be checked when initially rendered. */ defaultValue?: string[] /** The controlled value of checked items. */ value?: string[] /** Called when the checked items change. */ onValueChange?: (value: string[]) => void /** When `true`, prevents the user from interacting with the checkboxes. */ disabled?: boolean /** Specifies whether the checkbox group is in an error state */ error?: boolean /** Error messages to display */ errorMessages?: string | string[] /** Label for the group */ label?: string /** Hint text for the group */ hint?: string /** The orientation of the checkboxes */ orientation?: 'horizontal' | 'vertical' /** Whether to show a border around the group */ bordered?: boolean /** Density of the checkboxes */ density?: 'compact' | 'default' | 'comfortable' /** Options to render as checkboxes automatically */ options?: (string | CheckboxOption)[] /** Name attribute for all checkboxes in the group */ name?: string /** Inline layout (alias for horizontal) */ inline?: boolean } const CheckboxGroup = React.forwardRef<HTMLDivElement, CheckboxGroupProps>( ( { className, defaultValue, value, onValueChange, disabled, error, errorMessages, label, hint, orientation = 'vertical', bordered = false, density = 'default', options, name, inline, children, ...props }, ref, ) => { const [internalValue, setInternalValue] = React.useState<string[]>(defaultValue ?? []) const isControlled = value !== undefined const selected = isControlled ? value : internalValue const setSelected = React.useCallback( (next: string[]) => { if (!isControlled) setInternalValue(next) onValueChange?.(next) }, [isControlled, onValueChange], ) const toggle = React.useCallback( (optionValue: string, checked: boolean | 'indeterminate') => { const isChecked = checked === true const next = isChecked ? [...selected, optionValue] : selected.filter((v) => v !== optionValue) setSelected(next) }, [selected, setSelected], ) const actualOrientation = inline ? 'horizontal' : orientation return ( <div ref={ref} data-slot="checkbox-group" role="group" data-orientation={actualOrientation} className={cn( 'flex flex-col gap-2', actualOrientation === 'horizontal' ? 'flex-row items-center' : 'flex-col', bordered && 'rounded-lg border p-4', className, )} {...props} > {label && <label className="text-sm font-medium">{label}</label>} {hint && !error && <p className="text-muted-foreground text-xs">{hint}</p>} <div className={cn( 'flex gap-4', actualOrientation === 'horizontal' ? 'flex-row flex-wrap items-center' : 'flex-col', )} > {options && options.length > 0 ? options.map((option, i) => { const optValue = typeof option === 'string' ? option : option.value const optLabel = typeof option === 'string' ? option : option.label const optDisabled = typeof option === 'string' ? undefined : option.disabled return ( <Checkbox key={i} value={optValue} label={optLabel} disabled={disabled || optDisabled} name={name} density={density} checked={selected.includes(optValue)} onCheckedChange={(checked) => toggle(optValue, checked)} /> ) }) : children} </div> {(error || errorMessages) && ( <div className="flex flex-col gap-0.5"> {typeof errorMessages === 'string' ? ( <p className="text-destructive text-xs">{errorMessages}</p> ) : ( errorMessages?.map((msg, i) => ( <p key={i} className="text-destructive text-xs"> {msg} </p> )) )} </div> )} </div> ) }, ) CheckboxGroup.displayName = 'CheckboxGroup' export { Checkbox, CheckboxGroup } -
components/ui/checkbox/index.ts 0.1 kB
export { Checkbox, CheckboxGroup, type CheckboxProps, type CheckboxGroupProps, type CheckboxOption, } from './checkbox'
Raw manifest: https://react.uipkge.dev/r/react/checkbox.json