shadimage
blode
uicheckboxsource: ok · refreshed

Checkbox

A control that allows the user to toggle between checked and unchecked states.

npx shadcn add "https://blode.co/ui/r/checkbox.json"
free

Where it goes

stats

Interaction

animated-counter

Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light
ui-primitives@base-ui/react

Files2 · source cached 10/7/2026, 3:06:01 AM

ui/checkbox.tsx
"use client";

import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox";
import * as React from "react";

import { cn } from "@/lib/utils";

import "./checkbox.css";

type CheckedState = boolean | "indeterminate";

export interface CheckboxProps extends Omit<
  React.ComponentProps<typeof CheckboxPrimitive.Root>,
  "checked" | "defaultChecked" | "indeterminate" | "onCheckedChange"
> {
  checked?: CheckedState;
  defaultChecked?: CheckedState;
  hasError?: boolean;
  indeterminate?: boolean;
  onCheckedChange?: (checked: CheckedState) => void;
}

const Checkbox = ({
  checked,
  className,
  defaultChecked,
  hasError,
  indeterminate,
  onCheckedChange,
  ...props
}: CheckboxProps) => {
  const resolvedIndeterminate =
    indeterminate ?? (checked === "indeterminate" || defaultChecked === "indeterminate");

  return (
    <CheckboxPrimitive.Root
      aria-invalid={hasError || undefined}
      checked={checked === "indeterminate" ? false : checked}
      className={cn(
        "ft-checkbox peer relative inline-flex size-5 shrink-0 cursor-pointer items-center justify-center overflow-hidden rounded-[6px] bg-card align-middle shadow-input transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 data-disabled:cursor-not-allowed data-checked:text-primary-foreground data-indeterminate:text-primary-foreground data-disabled:opacity-50",
        className,
      )}
      data-slot="checkbox"
      defaultChecked={defaultChecked === "indeterminate" ? false : defaultChecked}
      indeterminate={resolvedIndeterminate}
      onCheckedChange={(nextChecked) => onCheckedChange?.(nextChecked)}
      render={(renderProps, state) => {
        let dataState: "indeterminate" | "checked" | "unchecked" = "unchecked";
        if (state.indeterminate) {
          dataState = "indeterminate";
        } else if (state.checked) {
          dataState = "checked";
        }
        return <span {...renderProps} data-state={dataState} />;
      }}
      {...props}
    >
      <CheckboxPrimitive.Indicator
        className="pointer-events-none flex items-center justify-center text-current"
        data-slot="checkbox-indicator"
        keepMounted
      >
        <svg aria-hidden="true" className="z-10 h-3 w-4" role="presentation" viewBox="0 0 17 18">
          <polyline
            className="ft-checkbox-polyline"
            fill="none"
            points="1 9 7 14 15 4"
            stroke="currentColor"
            strokeDasharray={22}
            strokeLinecap="round"
            strokeLinejoin="round"
            strokeWidth={2}
          />
          <line
            className="ft-checkbox-line"
            stroke="currentColor"
            strokeLinecap="round"
            strokeWidth={2}
            x1="3"
            x2="13"
            y1="9"
            y2="9"
          />
        </svg>
      </CheckboxPrimitive.Indicator>
    </CheckboxPrimitive.Root>
  );
};

export { Checkbox };
ui/checkbox.css
.ft-checkbox[data-state="checked"] .ft-checkbox-polyline {
  stroke-dashoffset: 44;
  transition: stroke-dashoffset 250ms linear 0.2s;
}

.ft-checkbox:not([data-state="checked"]) .ft-checkbox-polyline {
  stroke-dashoffset: 66;
}

.ft-checkbox .ft-checkbox-line {
  opacity: 0;
  transform: scaleX(0);
  transform-origin: center;
  transition:
    opacity 250ms ease,
    transform 250ms ease;
}

.ft-checkbox[data-state="indeterminate"] .ft-checkbox-line {
  opacity: 1;
  transform: scaleX(1);
  transition:
    opacity 250ms ease 0.1s,
    transform 250ms ease 0.1s;
}

.ft-checkbox[data-state="indeterminate"] .ft-checkbox-polyline {
  opacity: 0;
}

.ft-checkbox::after {
  position: absolute;
  inset: 0;
  content: "";
  background-color: var(--primary);
  border-radius: 6px;
  opacity: 0;
  transform-origin: center;
  scale: 0.5;
  transition: all 200ms linear;
}

.ft-checkbox[data-state="checked"]::after,
.ft-checkbox[data-state="indeterminate"]::after {
  opacity: 1;
  scale: 1;
}

.ft-checkbox[data-state="checked"]::before,
.ft-checkbox[data-state="indeterminate"]::before {
  border-color: var(--primary);
}

.ft-checkbox::before {
  position: absolute;
  inset: 0;
  content: "";
  border: 1px solid var(--input);
  border-radius: 6px;
  transform-origin: center;
  transition: all;
}

.ft-checkbox:hover::before {
  border-color: var(--input-hover, var(--input));
}

More from blode

Browse all components in this library →