Checkbox
An accessible checkbox component for forms and selection lists. Supports indeterminate states, custom styling, and keyboard navigation, built on top of React Aria primitives.
Basic Usage
By clicking this checkbox, you agree to the terms and conditions.
import { Checkbox } from "@repo/ui/components/checkbox";
import { Label } from "@repo/ui/components/label";
export const Example1 = () => {
return (
<div className="flex flex-col gap-6">
<div className="flex items-center gap-3">
<Checkbox id="terms" />
<Label htmlFor="terms">Accept terms and conditions</Label>
</div>
<div className="flex items-start gap-3">
<Checkbox id="terms-2" defaultSelected />
<div className="space-y-2">
<Label htmlFor="terms-2">Accept terms and conditions</Label>
<p className="text-muted-foreground text-sm">
By clicking this checkbox, you agree to the terms and
conditions.
</p>
</div>
</div>
<div className="group-[data] flex items-start gap-3">
<Checkbox id="toggle" isDisabled />
<Label htmlFor="toggle">Enable notifications</Label>
</div>
<Label
htmlFor="toggle-2"
className="hover:bg-accent/50 has-aria-checked:border-primary has-aria-checked:bg-primary/20 dark:has-aria-checked:border-primary flex items-start gap-3 rounded-lg border p-3"
>
<Checkbox id="toggle-2" defaultSelected className="" />
<div className="grid gap-1.5 font-normal">
<p className="text-sm font-medium leading-none">
Enable notifications
</p>
<p className="text-muted-foreground text-sm">
You can enable or disable notifications at any time.
</p>
</div>
</Label>
</div>
);
};
Controlled
Checked: No
"use client";
import { useState } from "react";
import { Checkbox } from "@repo/ui/components/checkbox";
export const Example2 = () => {
const [checked, setChecked] = useState(false);
return (
<div className="space-x-2">
<div className="flex items-center gap-2">
<Checkbox isSelected={checked} onChange={setChecked} />
Controlled checkbox
</div>
<p>Checked: {checked ? "Yes" : "No"}</p>
</div>
);
};
Disabled
import { Checkbox } from "@repo/ui/components/checkbox";
export const Example3 = () => {
return (
<div className="flex flex-col space-y-2">
<div className="flex items-center gap-2">
<Checkbox isDisabled />
Disabled unchecked
</div>
<div className="flex items-center gap-2">
<Checkbox isDisabled isSelected />
Disabled checked
</div>
</div>
);
};
Indeterminate
"use client";
import { useState } from "react";
import { Checkbox } from "@repo/ui/components/checkbox";
import { Button } from "@repo/ui/components/button";
export const Example4 = () => {
const [selected, setSelected] = useState<boolean | "indeterminate">(
"indeterminate",
);
return (
<div className="space-y-4">
<div className="flex items-center gap-2">
<Checkbox
isIndeterminate={selected === "indeterminate"}
isSelected={selected === true}
onChange={(isSelected) => setSelected(isSelected)}
/>
Indeterminate checkbox
</div>
<div className="flex gap-2">
<Button
onPress={() => setSelected("indeterminate")}
variant="outline"
>
Set Indeterminate
</Button>
<Button onPress={() => setSelected(true)} variant="outline">
Set Checked
</Button>
<Button onPress={() => setSelected(false)} variant="outline">
Set Unchecked
</Button>
</div>
</div>
);
};
Component Code
"use client";
import { Check, Minus } from "lucide-react";
import {
Checkbox as AriaCheckbox,
CheckboxProps as AriaCheckboxProps,
composeRenderProps,
} from "react-aria-components";
import { tv, type VariantProps, cn } from "tailwind-variants";
const checkboxVariants = tv({
base: "ring-offset-background group-data-focus-visible:ring-ring border-primary group-data-focus-visible:outline-none group-data-focus-visible:ring-2 group-data-focus-visible:ring-offset-2 flex h-5 w-5 shrink-0 items-center justify-center overflow-hidden rounded-md border",
variants: {
size: {
default: "h-5 w-5",
sm: "h-4 w-4",
lg: "h-6 w-6",
},
},
defaultVariants: {
size: "default",
},
});
const checkboxIconVariants = tv({
base: "animate-in fade-in zoom-in transition-all duration-200 ease-out",
variants: {
size: {
default: "h-4 w-4",
sm: "h-3 w-3",
lg: "h-5 w-5",
},
},
defaultVariants: {
size: "default",
},
});
export interface CheckboxProps
extends Omit<AriaCheckboxProps, "children">,
VariantProps<typeof checkboxVariants> {
className?: string;
}
export const Checkbox: React.FC<CheckboxProps> = (props) => {
const { className, size, ...restProps } = props;
return (
<AriaCheckbox
{...restProps}
className={composeRenderProps(
className,
(className, { isDisabled }) =>
cn(
"group flex cursor-pointer items-center gap-2 text-sm [-webkit-tap-highlight-color:transparent]",
isDisabled && "cursor-not-allowed opacity-50",
className,
) || "",
)}
>
{({
isHovered,
isPressed,
isDisabled,
isSelected,
isIndeterminate,
}) => (
<span
className={cn(
checkboxVariants({ size }),
(isSelected || isIndeterminate) &&
"bg-primary text-primary-foreground",
)}
data-hovered={isHovered}
data-pressed={isPressed}
data-disabled={isDisabled}
data-state={isSelected ? "checked" : null}
aria-checked={isSelected}
aria-hidden="true"
>
{isIndeterminate ? (
<Minus className={checkboxIconVariants({ size })} />
) : isSelected ? (
<Check className={checkboxIconVariants({ size })} />
) : null}
</span>
)}
</AriaCheckbox>
);
};
Checkbox.displayName = "Checkbox";
Card
A flexible card component for displaying grouped content like headers, images, and footers. Perfect for dashboards, profiles, and listings, with built-in dark mode and responsiveness.
ColorArea
A sophisticated color area component for selecting 2D color values. Features accessible keyboard navigation, touch support, and seamless integration with other color primitives.