AniUI

Chip

Interactive tags for filters, categories, and multi-select. Unlike Badge (display-only), Chips are pressable and selectable.

import { Chip } from "@/components/ui/chip";
<Chip variant="default">React Native</Chip>
<Chip variant="secondary">TypeScript</Chip>
<Chip variant="outline">NativeWind</Chip>

Installation

npx @aniui/cli add chip

Variants

<Chip variant="default">Default</Chip>
<Chip variant="secondary">Secondary</Chip>
<Chip variant="outline">Outline</Chip>
<Chip variant="destructive">Destructive</Chip>

Sizes

<Chip size="sm">Small</Chip>
<Chip size="md">Medium</Chip>
<Chip size="lg">Large</Chip>

Selectable

Use the selected prop for filter chips. Selected chips switch to the default (filled) variant automatically.

const [selected, setSelected] = useState<string[]>(["react-native"]);
const toggle = (id: string) =>
  setSelected((s) => s.includes(id) ? s.filter((x) => x !== id) : [...s, id]);
<Chip selected={selected.includes("react-native")} onPress={() => toggle("react-native")}>React Native</Chip>
<Chip selected={selected.includes("expo")} onPress={() => toggle("expo")}>Expo</Chip>
<Chip selected={selected.includes("nativewind")} onPress={() => toggle("nativewind")}>NativeWind</Chip>

Closable

Add an onClose handler to show a remove button.

<Chip onClose={() => console.log("removed")}>Removable</Chip>
<Chip variant="secondary" onClose={() => {}}>Tag</Chip>

Props

PropTypeDefault
variant
"default" | "secondary" | "outline" | "destructive"
"outline"
size
"sm" | "md" | "lg"
"md"
children
string
selected
boolean
false
onClose
() => void
textClassName
string
className
string

Source

components/ui/chip.tsx
import React from "react";
import { View, Text, Pressable } from "react-native";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";

const chipVariants = cva(
  "flex-row items-center rounded-full min-h-8",
  {
    variants: {
      variant: {
        default: "bg-primary",
        secondary: "bg-secondary",
        outline: "border border-input bg-transparent",
        destructive: "bg-destructive",
      },
      size: {
        sm: "px-2.5 py-1 gap-1",
        md: "px-3 py-1.5 gap-1.5",
        lg: "px-4 py-2 gap-2",
      },
    },
    defaultVariants: { variant: "default", size: "md" },
  }
);
const chipTextVariants = cva("font-medium", {
  variants: {
    variant: {
      default: "text-primary-foreground",
      secondary: "text-secondary-foreground",
      outline: "text-foreground",
      destructive: "text-destructive-foreground",
    },
    size: { sm: "text-xs", md: "text-sm", lg: "text-base" },
  },
  defaultVariants: { variant: "default", size: "md" },
});
export interface ChipProps
  extends React.ComponentPropsWithoutRef<typeof Pressable>,
    VariantProps<typeof chipVariants> {
  className?: string;
  textClassName?: string;
  children: string;
  selected?: boolean;
  onClose?: () => void;
}
export function Chip({ variant, size, className, textClassName, children, selected, onClose, ...props }: ChipProps) {
  const v = selected ? "default" : (variant ?? "outline");
  return (
    <Pressable className={cn(chipVariants({ variant: v, size }), className)} accessible={true} accessibilityRole="button" accessibilityState={{ selected }} {...props}>
      <Text className={cn(chipTextVariants({ variant: v, size }), textClassName)}>{children}</Text>
      {onClose && (
        <Pressable onPress={onClose} accessibilityRole="button" accessibilityLabel={`Remove ${children}`} className="ml-0.5">
          <Text className={cn("text-xs", v === "outline" ? "text-muted-foreground" : chipTextVariants({ variant: v, size: "sm" }))}>✕</Text>
        </Pressable>
      )}
    </Pressable>
  );
}