robertlib

Chip

Component

About

A small rounded label for a status or tag that can optionally be toggled (selected/pressed) or dismissed with a built-in remove button.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Chip to your project and fits it to your theme.

Code

How Chip is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

import Chip from "@/components/ui/chip";
import { useState } from "react";

const invoices = [
  { number: "2026-0141", status: "unpaid" },
  { number: "2026-0142", status: "overdue" },
  { number: "2026-0143", status: "paid" },
  { number: "2026-0144", status: "unpaid" },
  { number: "2026-0145", status: "paid" },
];

const statuses = [
  { label: "Unpaid", value: "unpaid" },
  { label: "Overdue", value: "overdue" },
  { label: "Paid", value: "paid" },
];

export default function Filter() {
  const [selected, setSelected] = useState(["unpaid", "overdue"]);
  const shown = invoices.filter((invoice) => selected.includes(invoice.status));

  return (
    <div className="space-y-3">
      <div aria-label="Status" className="flex flex-wrap gap-2" role="group">
        {statuses.map((status) => (
          <Chip
            color="primary"
            key={status.value}
            onSelectedChange={(isSelected) =>
              setSelected((current) =>
                isSelected
                  ? [...current, status.value]
                  : current.filter((value) => value !== status.value),
              )
            }
            selected={selected.includes(status.value)}
          >
            {status.label}
          </Chip>
        ))}
      </div>
      <p className="text-sm text-neutral-600 dark:text-neutral-400">
        {shown.length} of {invoices.length} invoices:{" "}
        {shown.map((invoice) => invoice.number).join(", ") || "none"}
      </p>
    </div>
  );
}

Dependencies