Skip to main content

Overview

The UI component library consists of accessible, customizable primitives built on Radix UI and styled with Tailwind CSS. All components support the asChild pattern for polymorphic rendering and include full TypeScript support.

Button

A versatile button component with multiple variants and sizes.

Props

variant
string
default:"default"
Button style variant: default | destructive | outline | secondary | ghost | link
size
string
default:"default"
Button size: default | sm | lg | icon
asChild
boolean
default:"false"
Render as a child component (useful for links)

Usage Examples

import { Button } from "@/components/ui/button";

export function Example() {
  return <Button>Click me</Button>;
}

Implementation

src/components/ui/button.tsx
const buttonVariants = cva(
  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
        destructive: "bg-destructive text-white shadow-xs hover:bg-destructive/90",
        outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground",
        secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
        ghost: "hover:bg-accent hover:text-accent-foreground",
        link: "text-primary underline-offset-4 hover:underline"
      },
      size: {
        default: "h-9 px-4 py-2 has-[>svg]:px-3",
        sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
        lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
        icon: "size-9"
      }
    },
    defaultVariants: {
      variant: "default",
      size: "default"
    }
  }
);

Card

A flexible card component with compound components for header, content, and footer.

Components

  • Card: Root container
  • CardHeader: Header section with title and description
  • CardTitle: Card title
  • CardDescription: Card description
  • CardAction: Action area in header (positioned in top-right)
  • CardContent: Main content area
  • CardFooter: Footer section

Usage Examples

import {
  Card,
  CardHeader,
  CardTitle,
  CardDescription,
  CardContent,
  CardFooter
} from "@/components/ui/card";
import { Button } from "@/components/ui/button";

export function Example() {
  return (
    <Card>
      <CardHeader>
        <CardTitle>Project Title</CardTitle>
        <CardDescription>A brief description of the project</CardDescription>
      </CardHeader>
      <CardContent>
        <p>Main content goes here...</p>
      </CardContent>
      <CardFooter>
        <Button>View Details</Button>
      </CardFooter>
    </Card>
  );
}

Badge

Compact labels for tags, status indicators, and metadata.

Props

variant
string
default:"default"
Badge style: default | secondary | destructive | outline
asChild
boolean
default:"false"
Render as a child component

Usage Examples

import { Badge } from "@/components/ui/badge";

export function Example() {
  return (
    <div className="flex gap-2">
      <Badge>Default</Badge>
      <Badge variant="secondary">Secondary</Badge>
      <Badge variant="destructive">Error</Badge>
      <Badge variant="outline">Outline</Badge>
    </div>
  );
}

Dialog

Accessible modal dialog component with overlay.

Components

  • Dialog: Root component
  • DialogTrigger: Button to open dialog
  • DialogContent: Dialog content container
  • DialogHeader: Header section
  • DialogTitle: Dialog title
  • DialogDescription: Dialog description
  • DialogFooter: Footer section
  • DialogClose: Close button

Props

showCloseButton
boolean
default:"true"
Show the close button in the top-right corner

Usage Examples

import {
  Dialog,
  DialogTrigger,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogDescription,
  DialogFooter
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";

export function Example() {
  return (
    <Dialog>
      <DialogTrigger asChild>
        <Button>Open Dialog</Button>
      </DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Are you sure?</DialogTitle>
          <DialogDescription>
            This action cannot be undone. This will permanently delete your account.
          </DialogDescription>
        </DialogHeader>
        <DialogFooter>
          <Button variant="outline">Cancel</Button>
          <Button variant="destructive">Delete Account</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

Form

Integrated form components with React Hook Form validation.

Components

  • Form: Root form provider (wraps React Hook Form’s FormProvider)
  • FormField: Field wrapper with Controller
  • FormItem: Field container
  • FormLabel: Field label
  • FormControl: Input control wrapper
  • FormDescription: Help text
  • FormMessage: Error message

Usage Examples

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
import {
  Form,
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";

const formSchema = z.object({
  email: z.string().email("Invalid email address"),
  name: z.string().min(2, "Name must be at least 2 characters")
});

export function ContactForm() {
  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      email: "",
      name: ""
    }
  });

  function onSubmit(values: z.infer<typeof formSchema>) {
    console.log(values);
  }

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
        <FormField
          control={form.control}
          name="name"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Name</FormLabel>
              <FormControl>
                <Input placeholder="John Doe" {...field} />
              </FormControl>
              <FormDescription>
                Your full name as it appears on official documents.
              </FormDescription>
              <FormMessage />
            </FormItem>
          )}
        />
        <FormField
          control={form.control}
          name="email"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Email</FormLabel>
              <FormControl>
                <Input type="email" placeholder="[email protected]" {...field} />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />
        <Button type="submit">Submit</Button>
      </form>
    </Form>
  );
}

Input

Text input field with consistent styling.

Usage Examples

import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";

export function Example() {
  return (
    <div className="grid gap-2">
      <Label htmlFor="email">Email</Label>
      <Input id="email" type="email" placeholder="[email protected]" />
    </div>
  );
}

Label

Accessible form label component.

Usage

import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";

export function Example() {
  return (
    <div className="grid gap-2">
      <Label htmlFor="username">Username</Label>
      <Input id="username" placeholder="@username" />
    </div>
  );
}

Select

Dropdown select component with custom styling.

Components

  • Select: Root component
  • SelectTrigger: Button to open dropdown
  • SelectValue: Selected value display
  • SelectContent: Dropdown content
  • SelectItem: Individual option
  • SelectGroup: Group options
  • SelectLabel: Group label
  • SelectSeparator: Visual separator

Props

size
string
default:"default"
Trigger size: default | sm
chevron
boolean
default:"true"
Show chevron icon in trigger

Usage Examples

import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue
} from "@/components/ui/select";

export function Example() {
  return (
    <Select>
      <SelectTrigger>
        <SelectValue placeholder="Select a theme" />
      </SelectTrigger>
      <SelectContent>
        <SelectItem value="light">Light</SelectItem>
        <SelectItem value="dark">Dark</SelectItem>
        <SelectItem value="system">System</SelectItem>
      </SelectContent>
    </Select>
  );
}

Sheet

Slide-out panel component (mobile drawer/sidebar).

Components

  • Sheet: Root component
  • SheetTrigger: Button to open sheet
  • SheetContent: Sheet content container
  • SheetHeader: Header section
  • SheetTitle: Sheet title
  • SheetDescription: Sheet description
  • SheetFooter: Footer section
  • SheetClose: Close button

Props

side
string
default:"right"
Sheet position: top | right | bottom | left

Usage Examples

import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger
} from "@/components/ui/sheet";
import { Button } from "@/components/ui/button";

export function Example() {
  return (
    <Sheet>
      <SheetTrigger asChild>
        <Button variant="outline">Open Menu</Button>
      </SheetTrigger>
      <SheetContent side="right">
        <SheetHeader>
          <SheetTitle>Menu</SheetTitle>
          <SheetDescription>
            Navigate through the application
          </SheetDescription>
        </SheetHeader>
        <div className="py-4">
          {/* Navigation items */}
        </div>
      </SheetContent>
    </Sheet>
  );
}

Textarea

Multi-line text input component.

Usage

import { Textarea } from "@/components/ui/textarea";
import { Label } from "@/components/ui/label";

export function Example() {
  return (
    <div className="grid gap-2">
      <Label htmlFor="message">Message</Label>
      <Textarea id="message" placeholder="Type your message here..." />
    </div>
  );
}

Context menu and dropdown component.

Components

  • DropdownMenu: Root component
  • DropdownMenuTrigger: Button to open menu
  • DropdownMenuContent: Menu content container
  • DropdownMenuItem: Individual menu item
  • DropdownMenuCheckboxItem: Checkbox item
  • DropdownMenuRadioGroup: Radio group
  • DropdownMenuRadioItem: Radio item
  • DropdownMenuLabel: Section label
  • DropdownMenuSeparator: Visual separator
  • DropdownMenuShortcut: Keyboard shortcut display
  • DropdownMenuSub: Submenu container
  • DropdownMenuSubTrigger: Submenu trigger
  • DropdownMenuSubContent: Submenu content

Usage Examples

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";

export function Example() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="outline">Open Menu</Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuLabel>My Account</DropdownMenuLabel>
        <DropdownMenuSeparator />
        <DropdownMenuItem>Profile</DropdownMenuItem>
        <DropdownMenuItem>Settings</DropdownMenuItem>
        <DropdownMenuItem>Logout</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Styling and Theming

All components respect the global theme configuration defined in your Tailwind config:
globals.css
@layer base {
  :root {
    --background: 0 0% 100%;
    --foreground: 222.2 84% 4.9%;
    --primary: 222.2 47.4% 11.2%;
    --primary-foreground: 210 40% 98%;
    /* ... more CSS variables */
  }
}

Custom Styling

All components accept a className prop for custom styling:
<Button className="bg-gradient-to-r from-purple-500 to-pink-500">
  Custom Gradient Button
</Button>
Use the cn() utility when combining multiple class names to avoid style conflicts:
import { cn } from "@/utils/cn";

<Button className={cn("custom-class", condition && "conditional-class")}>
  Button
</Button>

Accessibility Features

All components include built-in accessibility features:
  • Keyboard Navigation: Full keyboard support for all interactive components
  • ARIA Attributes: Proper ARIA labels, roles, and states
  • Focus Management: Visible focus indicators and focus trapping in modals
  • Screen Reader Support: Descriptive labels and announcements
All Radix UI primitives are tested against the WAI-ARIA authoring practices.

Next Steps

Animated Background

Learn about the canvas-based animated grid component

GitHub Stats

Explore the GitHub statistics integration

Build docs developers (and LLMs) love