Checkbox
The checkbox component
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";