Checkbox
A simple native checkbox prepared for indeterminate states
import { Checkbox } from '@/components/checkbox';
export default function CheckboxPreview() {
return <Checkbox />;
} Dependencies
Source Code
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: [
'focus-visible:ring-(length:--ring-width) 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 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
import { Checkbox } from '@/components/checkbox';
const labelClass = 'font-medium text-base text-foreground';
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 className={labelClass} htmlFor="unchecked">
Unchecked
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="checked" checked onChange={() => {}} />
<label className={labelClass} htmlFor="checked">
Checked
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="indeterminate" indeterminate />
<label className={labelClass} htmlFor="indeterminate">
Indeterminate
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="disabled" disabled />
<label className={labelClass} htmlFor="disabled">
Disabled
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="disabled-checked" checked disabled />
<label className={labelClass} htmlFor="disabled-checked">
Checked Disabled
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="disabled-indeterminate" indeterminate disabled />
<label className={labelClass} 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