Checkbox
A simple native checkbox prepared for indeterminate states
import { Checkbox } from '@/components/checkbox';
export default function CheckboxPreview() {
return <Checkbox />;
} Dependencies
Source Code
'use client';
import { useEffect, useRef } from 'react';
import { composeRefs } from '@/lib/compose-refs';
import { cva } from '@/lib/utils/classnames';
interface CheckboxProps
extends Omit<React.ComponentPropsWithRef<'input'>, 'type'> {
indeterminate?: boolean;
}
const checkboxStyle = cva({
base: [
'relative flex size-5 shrink-0 appearance-none items-center justify-center rounded-sm border border-border bg-background shadow-xs outline-none ring-ring transition focus-visible:ring-4 enabled:cursor-pointer enabled:not-checked:hover:border-mix-border/8',
// checked
'checked:enabled:hover:mix-with-accent-foreground checked:enabled:border-accent checked:enabled:bg-accent checked:enabled:hover:border-mix-accent/8',
// indeterminate
'indeterminate:enabled:hover:mix-with-accent-foreground indeterminate:enabled:border-accent indeterminate:enabled:bg-accent indeterminate:enabled:hover:border-mix-accent/8',
// checked checkmark
"before:absolute before:text-accent-foreground checked:before:font-bold checked:before:text-xs checked:before:content-['✓']",
// indeterminate dash
'indeterminate:before:h-0.5 indeterminate:before:w-1.5 indeterminate:before:bg-accent-foreground',
// disabled
'disabled:cursor-not-allowed disabled:border-foreground/5 disabled:bg-foreground/10 disabled:indeterminate:before:bg-foreground/50 disabled:checked:before:text-foreground/50',
],
});
const Checkbox = ({
ref,
indeterminate,
className,
...props
}: CheckboxProps) => {
const internalRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (internalRef.current) {
internalRef.current.indeterminate = !!indeterminate;
}
}, [indeterminate]);
return (
<input
type="checkbox"
ref={composeRefs(ref, internalRef)}
className={checkboxStyle({ className })}
{...props}
/>
);
};
export { Checkbox }; API Reference
Extends the native input element with type="checkbox".
Examples
Simple
import { Checkbox } from '@/components/checkbox';
export default function CheckboxPreview() {
return <Checkbox />;
} Disabled
import { Checkbox } from '@/components/checkbox';
export default function CheckboxDisabledPreview() {
return (
<div className="flex flex-col space-y-4">
<Checkbox disabled />
<Checkbox checked disabled />
</div>
);
} All states
'use client';
import { Checkbox } from '@/components/checkbox';
import { Label } from '@/components/label';
export default function CheckboxAllStatesPreview() {
return (
<div className="flex flex-col space-y-4">
<div className="flex items-center space-x-2">
<Checkbox id="unchecked" />
<Label htmlFor="unchecked">Unchecked</Label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="checked" checked onChange={() => {}} />
<Label htmlFor="checked">Checked</Label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="indeterminate" indeterminate />
<Label htmlFor="indeterminate">Indeterminate</Label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="disabled" disabled />
<Label htmlFor="disabled">Disabled</Label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="disabled-checked" checked disabled />
<Label htmlFor="disabled-checked">Checked Disabled</Label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="disabled-indeterminate" indeterminate disabled />
<Label htmlFor="disabled-indeterminate">Indeterminate Disabled</Label>
</div>
</div>
);
} Best Practices
-
Labels:
- Always provide clear labels
- Make labels clickable
- Use consistent label positioning
-
Accessibility:
- Ensure keyboard navigation
- Use fieldset for groups
Previous
Calendar
Next
Color Picker