Component Libraries
Build reusable UI components with React, Vue, and framework-agnostic approaches
What is a Component Library?
A component library is a collection of reusable UI components that implement your design system. Components are the building blocks of your interfaceβbuttons, inputs, modals, cards, and moreβpackaged for consistent use across your applications.
π¦ Component Library Approaches
Pre-styled, opinionated (Ant Design, MUI)
Logic only, you add styles (Radix, Headless UI)
Components you own (shadcn/ui)
Building a Basic Component
// components/Button/Button.tsx
import React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const buttonVariants = cva(
// Base styles
'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
primary: 'bg-primary text-white hover:bg-primary/90',
secondary: 'bg-secondary text-white hover:bg-secondary/90',
outline: 'border border-input bg-transparent hover:bg-accent',
ghost: 'hover:bg-accent hover:text-accent-foreground',
destructive: 'bg-destructive text-white hover:bg-destructive/90',
},
size: {
sm: 'h-8 px-3 text-xs',
md: 'h-10 px-4 text-sm',
lg: 'h-12 px-6 text-base',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'primary',
size: 'md',
},
}
);
export interface ButtonProps
extends React.ButtonHTMLAttributes,
VariantProps {
isLoading?: boolean;
}
export const Button = React.forwardRef(
({ className, variant, size, isLoading, children, disabled, ...props }, ref) => {
return (
);
}
);
Button.displayName = 'Button';
// Usage
Compound Components Pattern
For complex components like dropdowns or tabs, use the compound component pattern for flexibility:
// components/Tabs/Tabs.tsx
import React, { createContext, useContext, useState } from 'react';
// Context for shared state
const TabsContext = createContext<{
activeTab: string;
setActiveTab: (id: string) => void;
} | null>(null);
// Root component
export function Tabs({ defaultValue, children }: {
defaultValue: string;
children: React.ReactNode;
}) {
const [activeTab, setActiveTab] = useState(defaultValue);
return (
{children}
);
}
// Tab list
export function TabList({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
// Individual tab trigger
export function TabTrigger({ value, children }: {
value: string;
children: React.ReactNode;
}) {
const context = useContext(TabsContext);
if (!context) throw new Error('TabTrigger must be used within Tabs');
const { activeTab, setActiveTab } = context;
const isActive = activeTab === value;
return (
);
}
// Tab content panel
export function TabContent({ value, children }: {
value: string;
children: React.ReactNode;
}) {
const context = useContext(TabsContext);
if (!context) throw new Error('TabContent must be used within Tabs');
if (context.activeTab !== value) return null;
return (
{children}
);
}
// Usage - Clean, flexible API
Account
Settings
Notifications
Account content here...
Settings content here...
Notifications content here...
Headless Components
Headless components provide behavior and accessibility without styling. Radix UI is the most popular choice:
// Using Radix UI Primitives
import * as Dialog from '@radix-ui/react-dialog';
import { X } from 'lucide-react';
export function Modal({ trigger, title, children }) {
return (
{trigger}
{title}
{children}
);
}
// Usage
Open Modal}
title="Edit Profile"
>
Modal content here...
Component Organization
components/
βββ primitives/ # Base building blocks
β βββ Button/
β β βββ Button.tsx
β β βββ Button.test.tsx
β β βββ Button.stories.tsx
β β βββ index.ts
β βββ Input/
β βββ Select/
βββ composed/ # Built from primitives
β βββ SearchBar/ # Input + Button
β βββ Combobox/ # Input + Select + List
β βββ DatePicker/
βββ patterns/ # Common UI patterns
β βββ DataTable/
β βββ Form/
β βββ Sidebar/
βββ index.ts # Barrel exports
// index.ts - Export everything
export * from './primitives/Button';
export * from './primitives/Input';
export * from './composed/SearchBar';
// ...
Popular Component Library Tools
Class Variance Authority (CVA)
Create type-safe component variants with Tailwind CSS.
cva.style βRadix UI
Unstyled, accessible primitives for React. Foundation for shadcn/ui.
radix-ui.com βHeadless UI
Unstyled components from the Tailwind CSS team.
headlessui.com βReact Aria (Adobe)
Accessibility primitives and hooks for building inclusive UIs.
react-spectrum.adobe.com βπ‘ Best Practices
- β’ Use
forwardRefso components work with form libraries and refs - β’ Make components accessible by default (ARIA, keyboard navigation)
- β’ Use composition over configurationβprefer compound components
- β’ Consider headless libraries (Radix, React Aria) for complex interactions
- β’ Write stories in Storybook for every component variation
- β’ Test components in isolation with unit tests and visual regression tests