Volver a Sistemas de Diseño
Tema 3 de 8
Bibliotecas de Componentes
Construye, organiza y mantén bibliotecas de componentes reutilizables
¿Qué es una Biblioteca de Componentes?
Una biblioteca de componentes es una colección de componentes de interfaz reutilizables (botones, entradas, tarjetas) que encapsulan tanto estilos como comportamiento. Es el corazón de tu sistema de diseño.
🏗️ Tres Enfoques
1. Componentes Estilizados (Opinados)
Completamente estilizados, listos para usar. Ejemplo: Chakra UI, Material UI
✅ Rápido de adoptar ⚠️ Menos flexible
2. Componentes Sin Cabeza (Sin Estilo)
Solo lógica y accesibilidad, tú manejas los estilos. Ejemplo: Radix UI, React Aria
✅ Control total ⚠️ Más trabajo inicial
3. Copiar & Pegar
Componentes que copias a tu base de código. Ejemplo: shadcn/ui
✅ Propiedad completa ⚠️ Sin actualizaciones automáticasConstruyendo un Componente Básico
// Button.tsx con class-variance-authority (CVA)
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const buttonVariants = cva(
// Estilos base
'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:opacity-50 disabled:pointer-events-none',
{
variants: {
variant: {
primary: 'bg-primary text-white hover:bg-primary/90',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
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-9 px-3 text-sm',
md: 'h-10 px-4 py-2',
lg: 'h-11 px-8 text-lg',
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, ...props }, ref) => {
return (
);
}
);
Button.displayName = 'Button';
// Uso
Componentes Compuestos
// Tabs.tsx (Patrón de componente compuesto)
import React, { createContext, useContext, useState } from 'react';
type TabsContextValue = {
activeTab: string;
setActiveTab: (tab: string) => void;
};
const TabsContext = createContext(undefined);
function useTabs() {
const context = useContext(TabsContext);
if (!context) throw new Error('Los componentes Tab deben usarse dentro de Tabs');
return context;
}
export function Tabs({ defaultValue, children }: { defaultValue: string; children: React.ReactNode }) {
const [activeTab, setActiveTab] = useState(defaultValue);
return (
{children}
);
}
export function TabsList({ children }: { children: React.ReactNode }) {
return {children};
}
export function TabsTrigger({ value, children }: { value: string; children: React.ReactNode }) {
const { activeTab, setActiveTab } = useTabs();
const isActive = activeTab === value;
return (
);
}
export function TabsContent({ value, children }: { value: string; children: React.ReactNode }) {
const { activeTab } = useTabs();
if (activeTab !== value) return null;
return {children};
}
// Uso
General
Seguridad
Facturación
Configuración general...
Configuración de seguridad...
Configuración de facturación...
Componentes Sin Cabeza con Radix UI
npm install @radix-ui/react-dialog
// Dialog.tsx (construido sobre Radix)
import * as DialogPrimitive from '@radix-ui/react-dialog';
import { cn } from '@/lib/utils';
export const Dialog = DialogPrimitive.Root;
export const DialogTrigger = DialogPrimitive.Trigger;
export const DialogContent = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, children, ...props }, ref) => (
{children}
));
// Uso
Organización de Componentes
components/
├── ui/ # Componentes primitivos
│ ├── Button/
│ │ ├── Button.tsx
│ │ ├── Button.test.tsx
│ │ ├── Button.stories.tsx
│ │ └── index.ts
│ ├── Input/
│ ├── Card/
│ └── Dialog/
├── patterns/ # Composiciones de componentes
│ ├── LoginForm/
│ ├── DataTable/
│ └── Navbar/
├── layouts/ # Componentes de diseño
│ ├── Container/
│ ├── Grid/
│ └── Stack/
└── providers/ # Componentes de contexto
├── ThemeProvider/
└── ToastProvider/
Herramientas Populares
💡 Mejores Prácticas
- • Comienza con componentes atómicos (Botón, Entrada) antes de los complejos
- • Usa componentes compuestos para APIs flexibles (Tabs, Dropdown)
- • Escribe tipos TypeScript para todos los props
- • Incluye accesibilidad (ARIA, navegación por teclado) desde el principio
- • Documenta con Storybook o ejemplos en línea
- • Prueba con jest + testing-library