TechLead
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áticas

Construyendo 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

  
    
  
  
    

¿Eliminar elemento?

Esta acción no se puede deshacer.

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/

💡 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