tutoriales.com

¡Construyendo Componentes Reutilizables y Estilizados con Tailwind CSS en Next.js App Router! 💅

Este tutorial te guiará paso a paso en la construcción de componentes React reutilizables dentro de un proyecto Next.js utilizando el App Router. Aprenderás a estilizar estos componentes de manera eficiente y escalable con Tailwind CSS, garantizando una interfaz de usuario consistente y un desarrollo ágil. Descubre las mejores prácticas para organizar tu código y maximizar la reutilización.

Intermedio18 min de lectura13 views
Reportar error

El desarrollo web moderno se basa en la modularidad y la reutilización. En el ecosistema React y Next.js, esto se traduce en la creación de componentes que pueden ser combinados y reusados a lo largo de toda la aplicación. Pero no basta con crear componentes; también necesitamos una estrategia robusta para estilizarlos de forma consistente y mantenible.

Aquí es donde Next.js App Router y Tailwind CSS brillan juntos. El App Router nos ofrece una estructura moderna y potente para organizar nuestras aplicaciones, mientras que Tailwind CSS nos proporciona una forma eficiente de aplicar estilos directamente en nuestro marcado HTML, eliminando la necesidad de escribir CSS personalizado complejo y garantizando la coherencia visual.

En este tutorial, exploraremos cómo construir componentes reutilizables desde cero y cómo aplicarles estilos con Tailwind CSS, aprovechando todas las ventajas que nos ofrece el App Router de Next.js. ¡Prepárate para llevar tus habilidades de desarrollo al siguiente nivel! 🚀


🎯 ¿Por qué Componentes Reutilizables y Tailwind CSS?

La combinación de componentes reutilizables y Tailwind CSS no es una casualidad; es una estrategia poderosa con múltiples beneficios:

✅ Beneficios de Componentes Reutilizables

  • Consistencia: Asegura que los elementos de la UI se vean y se comporten de manera uniforme en toda la aplicación.
  • Eficiencia: Reduce el tiempo de desarrollo al no tener que recrear los mismos elementos una y otra vez.
  • Mantenibilidad: Los cambios en un componente se propagan automáticamente a todas las instancias donde se usa, simplificando las actualizaciones.
  • Colaboración: Facilita el trabajo en equipo, ya que los desarrolladores pueden entender y usar los componentes existentes.

✨ Beneficios de Tailwind CSS

  • Desarrollo Rápido: Aplica estilos directamente en el HTML con clases de utilidad, eliminando la conmutación entre archivos CSS y HTML.
  • Tamaño Mínimo: Solo incluye el CSS que realmente utilizas, gracias a su proceso de purgado.
  • Diseño Responsivo Integrado: Utilidades para móviles primero que facilitan la creación de diseños adaptativos.
  • Consistencia de Diseño: Fomenta el uso de un sistema de diseño predefinido (colores, tipografías, espaciados) lo que lleva a una UI más coherente.
  • No Más Nombres de Clases: Olvídate de pensar en nombres semánticos para clases CSS. Las utilidades son descriptivas y directas.
📌 Nota: Tailwind CSS es un *framework CSS de primera utilidad*. Esto significa que, en lugar de clases predefinidas para componentes (como `btn` o `card`), proporciona clases de bajo nivel que puedes usar directamente en tu marcado para construir cualquier diseño.

🛠️ Configuración Inicial del Proyecto Next.js

Antes de empezar a crear componentes, necesitamos un proyecto Next.js con el App Router configurado y Tailwind CSS integrado.

Paso 1: Crear un Nuevo Proyecto Next.js

Si aún no tienes un proyecto, puedes crear uno nuevo con este comando:

npx create-next-app@latest my-next-components-app --typescript --eslint --tailwind --app

Durante la instalación, asegúrate de seleccionar Yes cuando te pregunte por Tailwind CSS y el App Router. Si por alguna razón no lo hiciste, o si ya tienes un proyecto existente, puedes agregar Tailwind manualmente.

💡 Consejo: El flag `--app` es crucial para inicializar el proyecto con el App Router de Next.js.

Paso 2: Verificar la Configuración de Tailwind CSS

Después de la instalación, revisa los archivos tailwind.config.ts y postcss.config.js en la raíz de tu proyecto. Deberían verse algo así:

tailwind.config.ts

import type { Config } from 'tailwindcss';

const config: Config = {
  content: [
    './pages/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
    './app/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};

export default config;

postcss.config.js

module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

También, asegúrate de que tu archivo app/globals.css importa las directivas de Tailwind:

app/globals.css

@tailwind base;
@tailwind components;
@tailwind utilities;

/* Otros estilos globales si los tienes */

¡Listo! Con esto, tu entorno está preparado para empezar a construir componentes.


📁 Estructura de Componentes en el App Router

Una buena organización es clave para la escalabilidad. En el App Router, la convención es mantener los componentes reutilizables en una carpeta components en la raíz del proyecto (o en src/components si usas la carpeta src).

my-next-components-app/
├── app/
│   ├── page.tsx
│   ├── layout.tsx
│   └── globals.css
├── components/
│   ├── Button/
│   │   └── Button.tsx
│   ├── Card/
│   │   └── Card.tsx
│   └── Navbar/
│       └── Navbar.tsx
├── public/
├── tailwind.config.ts
├── next.config.mjs
└── tsconfig.json

Esta estructura nos permite agrupar los archivos relacionados con un componente (como el propio componente, sus tipos, o sus estilos específicos si no usas solo Tailwind) en una única carpeta.

🏗️ Creando Nuestro Primer Componente: Button

Vamos a crear un componente de botón simple que sea reutilizable y configurable.

Paso 1: Crear el Archivo del Componente

Dentro de la carpeta components, crea una nueva carpeta Button y, dentro de ella, un archivo Button.tsx:

components/Button/Button.tsx

import React from 'react';

interface ButtonProps {
  children: React.ReactNode; // El contenido del botón
  onClick?: () => void;     // Manejador de clic opcional
  variant?: 'primary' | 'secondary' | 'danger'; // Tipos de botón
  size?: 'small' | 'medium' | 'large';      // Tamaños de botón
  className?: string;       // Clases adicionales para Tailwind
}

const Button: React.FC<ButtonProps> = ({
  children,
  onClick,
  variant = 'primary',
  size = 'medium',
  className = '',
}) => {
  const baseStyles = 'font-semibold rounded-md transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2';
  let variantStyles;
  let sizeStyles;

  switch (variant) {
    case 'primary':
      variantStyles = 'bg-blue-600 hover:bg-blue-700 text-white focus:ring-blue-500';
      break;
    case 'secondary':
      variantStyles = 'bg-gray-200 hover:bg-gray-300 text-gray-800 focus:ring-gray-400';
      break;
    case 'danger':
      variantStyles = 'bg-red-600 hover:bg-red-700 text-white focus:ring-red-500';
      break;
    default:
      variantStyles = 'bg-blue-600 hover:bg-blue-700 text-white focus:ring-blue-500';
  }

  switch (size) {
    case 'small':
      sizeStyles = 'px-3 py-1 text-sm';
      break;
    case 'medium':
      sizeStyles = 'px-4 py-2 text-base';
      break;
    case 'large':
      sizeStyles = 'px-6 py-3 text-lg';
      break;
    default:
      sizeStyles = 'px-4 py-2 text-base';
  }

  const combinedClasses = `${baseStyles} ${variantStyles} ${sizeStyles} ${className}`.trim();

  return (
    <button className={combinedClasses} onClick={onClick}>
      {children}
    </button>
  );
};

export default Button;

💡 Explicación del Código

  1. ButtonProps Interface: Define las propiedades que nuestro botón puede aceptar: children (el texto o contenido), onClick (un evento de clic), variant (para diferentes estilos visuales como primary, secondary, danger), size (para el tamaño del botón), y className para permitir que el consumidor del componente añada clases de Tailwind adicionales si lo necesita.
  2. baseStyles: Clases de Tailwind que se aplican a todos los botones, como font-semibold, rounded-md, y efectos de transición y enfoque.
  3. variantStyles: Usa un switch para aplicar estilos específicos basados en la prop variant. Esto permite cambiar fácilmente el color de fondo, texto y el anillo de enfoque.
  4. sizeStyles: Similar a variantStyles, pero para el tamaño, ajustando el padding y el text-size.
  5. combinedClasses: Combina todas las clases (baseStyles, variantStyles, sizeStyles y cualquier className pasado por el usuario). El método .trim() elimina cualquier espacio en blanco extra al principio o al final.
  6. return Statement: Renderiza un elemento <button> con todas las clases aplicadas y pasa el onClick y children directamente.

🌍 Usando el Componente Button en tu Aplicación

Ahora que tenemos nuestro componente Button, vamos a usarlo en nuestra página principal.

Modifica tu archivo app/page.tsx para importar y usar el Button:

app/page.tsx

import Button from '@/components/Button/Button'; // Asegúrate de que la ruta sea correcta

export default function Home() {
  return (
    <main className="flex min-h-screen flex-col items-center justify-center p-24 bg-gradient-to-br from-indigo-500 to-purple-600">
      <h1 className="text-5xl font-bold text-white mb-8">
        Componentes Reutilizables con Next.js y Tailwind CSS
      </h1>

      <div className="flex flex-wrap gap-4 justify-center">
        <Button onClick={() => alert('¡Primary click!')}>
          Botón Primario
        </Button>

        <Button variant="secondary" onClick={() => alert('¡Secondary click!')}>
          Botón Secundario
        </Button>

        <Button variant="danger" size="large" onClick={() => alert('¡Danger click!')}>
          Botón de Peligro Grande
        </Button>

        <Button variant="primary" size="small" className="shadow-lg">
          Botón Pequeño con Sombra
        </Button>

        <Button variant="secondary" size="medium" className="hover:scale-105 transform">
          Botón con Efecto Hover
        </Button>
      </div>
    </main>
  );
}

Ejecuta tu aplicación con npm run dev (o yarn dev) y visita http://localhost:3000. Deberías ver los botones renderizados con diferentes estilos basados en las props que les pasaste.

Page Component Contenedor de nivel superior Main <main> wrapper Div (Flex Container) display: flex; gap: 20px; Button Component Props: children: "Aceptar" variant: "primary" size: "md" className: "btn-1" Aceptar Button Component Props: children: "Cancelar" variant: "ghost" size: "md" className: "btn-2" Cancelar Button Component Props: children: "Borrar" variant: "danger" size: "sm" className: "btn-3" Borrar

💳 Creando un Componente Más Complejo: Card

Los botones son sencillos. Ahora, construyamos un componente Card más complejo que pueda mostrar una imagen, un título, una descripción y un botón de acción.

Paso 1: Crear el Archivo Card.tsx

Dentro de components, crea una nueva carpeta Card y, dentro de ella, un archivo Card.tsx:

components/Card/Card.tsx

import React from 'react';
import Button from '@/components/Button/Button'; // Importamos nuestro botón

interface CardProps {
  imageUrl: string;
  title: string;
  description: string;
  buttonText: string;
  onButtonClick: () => void;
  className?: string;
}

const Card: React.FC<CardProps> = ({
  imageUrl,
  title,
  description,
  buttonText,
  onButtonClick,
  className = '',
}) => {
  return (
    <div className={`max-w-sm rounded-lg overflow-hidden shadow-xl bg-white m-4 ${className}`}>
      {/* eslint-disable-next-line @next/next/no-img-element */}
      <img className="w-full h-48 object-cover" src={imageUrl} alt={title} />
      <div className="px-6 py-4">
        <div className="font-bold text-xl mb-2 text-gray-900">{title}</div>
        <p className="text-gray-700 text-base">
          {description}
        </p>
      </div>
      <div className="px-6 pt-4 pb-2">
        <Button onClick={onButtonClick} variant="primary" size="medium" className="w-full">
          {buttonText}
        </Button>
      </div>
    </div>
  );
};

export default Card;

💡 Explicación del Código de Card

  1. CardProps Interface: Define las propiedades para la imagen (imageUrl), título, descripción, texto del botón y su función onClick.
  2. Estructura HTML con Tailwind: Utilizamos clases de Tailwind para definir la apariencia de la tarjeta:
    • max-w-sm: Limita el ancho máximo.
    • rounded-lg: Bordes redondeados.
    • overflow-hidden: Asegura que los hijos (como la imagen) no desborden los bordes redondeados.
    • shadow-xl: Una sombra pronunciada para darle profundidad.
    • bg-white: Fondo blanco.
    • m-4: Margen alrededor de la tarjeta.
  3. Imagen: La etiqueta <img> usa w-full y h-48 para establecer el ancho y alto, y object-cover para asegurar que la imagen cubra el área sin distorsionarse.
  4. Contenido: Las clases px-6 py-4 y px-6 pt-4 pb-2 controlan el espaciado interno del texto y el botón, respectivamente.
  5. Reutilización del Button: Lo más importante es que dentro de nuestro Card, ¡estamos utilizando el componente Button que creamos anteriormente! Esto demuestra el poder de la reutilización.
⚠️ Advertencia: Para imágenes en Next.js, se recomienda usar el componente `next/image` por sus optimizaciones de rendimiento. Para simplificar el ejemplo, usamos `` estándar. Siéntete libre de reemplazarlo por `Image` de `next/image` y agregar las props `width` y `height` necesarias.

🚀 Usando el Componente Card

Ahora, volvamos a app/page.tsx para usar nuestro nuevo componente Card.

Modifica app/page.tsx de nuevo:

app/page.tsx

import Button from '@/components/Button/Button';
import Card from '@/components/Card/Card'; // Importamos el componente Card

export default function Home() {
  return (
    <main className="flex min-h-screen flex-col items-center justify-center p-8 bg-gradient-to-br from-indigo-500 to-purple-600">
      <h1 className="text-5xl font-bold text-white mb-12 text-center">
        Componentes Reutilizables con Next.js y Tailwind CSS
      </h1>

      <section className="mb-12">
        <h2 className="text-3xl font-semibold text-white mb-6">Nuestros Botones Personalizados</h2>
        <div className="flex flex-wrap gap-4 justify-center">
          <Button onClick={() => alert('¡Primary click!')}>
            Botón Primario
          </Button>
          <Button variant="secondary" onClick={() => alert('¡Secondary click!')}>
            Botón Secundario
          </Button>
          <Button variant="danger" size="large" onClick={() => alert('¡Danger click!')}>
            Botón de Peligro Grande
          </Button>
          <Button variant="primary" size="small" className="shadow-lg">
            Botón Pequeño con Sombra
          </Button>
        </div>
      </section>

      <section>
        <h2 className="text-3xl font-semibold text-white mb-6">Nuestras Tarjetas Flexibles</h2>
        <div className="flex flex-wrap justify-center">
          <Card
            imageUrl="https://images.unsplash.com/photo-1542438419-f79246101c70?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w1NzgyNDF8MHwxfHNlYXJjaHwxfHx0ZWNofGVufDB8fHx8MTcwMTg4OTgyNXww&ixlib=rb-4.0.3&q=80&w=1080"
            title="Desarrollo Frontend"
            description="Explora las últimas tendencias en desarrollo frontend con React, Next.js y Tailwind CSS para crear interfaces de usuario modernas y responsivas."
            buttonText="Aprender Más"
            onButtonClick={() => alert('¡Aprender más sobre Frontend!')}
          />
          <Card
            imageUrl="https://images.unsplash.com/photo-1522252234503-e356532cafd5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w1NzgyNDF8MHwxfHNlYXJjaHwxfHxjodingfGVufDB8fHx8MTcwMTg4OTgzMHww&ixlib=rb-4.0.3&q=80&w=1080"
            title="Backend con Node.js"
            description="Sumérgete en el mundo del desarrollo backend con Node.js, Express y bases de datos NoSQL para construir APIs robustas y escalables."
            buttonText="Descubrir"
            onButtonClick={() => alert('¡Descubrir Backend!')}
            className="border-4 border-yellow-300"
          />
          <Card
            imageUrl="https://images.unsplash.com/photo-1627398246342-972c738e4544?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w1NzgyNDF8MHwxfHNlYXJjaHwxfHxjbG91ZCUyMGNvbXB1dGluZ3xlbnwwfHx8fDE3MDE4ODk4MzN8MA&ixlib=rb-4.0.3&q=80&w=1080"
            title="Cloud Computing"
            description="Domina las plataformas de computación en la nube como AWS, Azure y Google Cloud para desplegar y escalar tus aplicaciones globalmente."
            buttonText="Empezar"
            onButtonClick={() => alert('¡Empezar con Cloud!')}
          />
        </div>
      </section>
    </main>
  );
}

Ahora, al recargar tu navegador, verás los botones y las tarjetas, cada una con su propia información y un botón funcional, todo estilizado con Tailwind CSS y construido con componentes reutilizables.

💅 Personalización y Extensión con Tailwind CSS

Una de las grandes ventajas de Tailwind CSS es su flexibilidad. Puedes extender o anular sus clases predeterminadas en tu archivo tailwind.config.ts.

Añadir Colores Personalizados

// tailwind.config.ts
import type { Config } from 'tailwindcss';

const config: Config = {
  content: [
    './pages/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
    './app/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  theme: {
    extend: {
      colors: {
        'custom-green': '#34D399', // Un tono de verde personalizado
        'custom-purple': '#8B5CF6', // Un tono de morado personalizado
      },
    },
  },
  plugins: [],
};

export default config;

Ahora puedes usar estas clases en tus componentes:

<Button className="bg-custom-green hover:bg-custom-purple">Mi Botón Custom</Button>

Añadir Fuentes Personalizadas

Puedes configurar fuentes personalizadas importándolas en globals.css y luego extendiéndolas en tailwind.config.ts.

app/globals.css

@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');

@tailwind base;
@tailwind components;
@tailwind utilities;

tailwind.config.ts

// tailwind.config.ts
// ... (código anterior)
  theme: {
    extend: {
      colors: {
        'custom-green': '#34D399',
        'custom-purple': '#8B5CF6',
      },
      fontFamily: {
        roboto: ['Roboto', 'sans-serif'], // Define una familia de fuentes
        // Aquí puedes añadir más fuentes
      },
    },
  },
// ... (resto del código)

Luego, puedes aplicarla:

<h1 className="font-roboto text-5xl">Mi Título con Roboto</h1>
🔥 Importante: Siempre que añadas o modifiques `tailwind.config.ts`, es una buena práctica reiniciar el servidor de desarrollo (`npm run dev`) para que los cambios se apliquen correctamente.

🧩 Composición Avanzada de Componentes

Una técnica poderosa en React es la composición, donde un componente acepta otros componentes o elementos como sus children. Esto permite crear componentes altamente flexibles.

Imagina que queremos un Modal (Dialog) que pueda contener cualquier tipo de contenido. No queremos que el Modal sepa qué contenido específico va a mostrar, solo que lo renderice.

Creando el Componente Modal

components/Modal/Modal.tsx

import React from 'react';

interface ModalProps {
  isOpen: boolean;
  onClose: () => void;
  children: React.ReactNode; // El contenido del modal
  title?: string;
}

const Modal: React.FC<ModalProps> = ({
  isOpen,
  onClose,
  children,
  title = 'Modal Título',
}) => {
  if (!isOpen) return null;

  return (
    <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
      <div className="bg-white rounded-lg shadow-xl max-w-lg w-full p-6 relative">
        <button
          onClick={onClose}
          className="absolute top-3 right-3 text-gray-500 hover:text-gray-700 text-2xl font-bold leading-none"
          aria-label="Cerrar Modal"
        >
          &times;
        </button>
        <h2 className="text-2xl font-bold mb-4 text-gray-900">{title}</h2>
        <div>
          {children}
        </div>
      </div>
    </div>
  );
};

export default Modal;

Usando el Componente Modal con Contenido Dinámico

app/page.tsx (añadiendo el estado y el uso del modal)

'use client'; // Marcar como Client Component para usar useState

import React, { useState } from 'react';
import Button from '@/components/Button/Button';
import Card from '@/components/Card/Card';
import Modal from '@/components/Modal/Modal'; // Importar el Modal

export default function Home() {
  const [isModalOpen, setIsModalOpen] = useState(false);

  return (
    <main className="flex min-h-screen flex-col items-center justify-center p-8 bg-gradient-to-br from-indigo-500 to-purple-600">
      <h1 className="text-5xl font-bold text-white mb-12 text-center">
        Componentes Reutilizables con Next.js y Tailwind CSS
      </h1>

      <section className="mb-12">
        <h2 className="text-3xl font-semibold text-white mb-6">Nuestros Botones Personalizados</h2>
        <div className="flex flex-wrap gap-4 justify-center">
          <Button onClick={() => alert('¡Primary click!')}>
            Botón Primario
          </Button>
          <Button variant="secondary" onClick={() => alert('¡Secondary click!')}>
            Botón Secundario
          </Button>
          <Button variant="danger" size="large" onClick={() => alert('¡Danger click!')}>
            Botón de Peligro Grande
          </Button>
          <Button variant="primary" size="small" className="shadow-lg">
            Botón Pequeño con Sombra
          </Button>
          <Button onClick={() => setIsModalOpen(true)} className="bg-emerald-500 hover:bg-emerald-600 text-white">
            Abrir Modal
          </Button>
        </div>
      </section>

      <section>
        <h2 className="text-3xl font-semibold text-white mb-6">Nuestras Tarjetas Flexibles</h2>
        <div className="flex flex-wrap justify-center">
          <Card
            imageUrl="https://images.unsplash.com/photo-1542438419-f79246101c70?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w1NzgyNDF8MHwxfHNlYXJjaHwxfHx0ZWNofGVufDB8fHx8MTcwMTg4OTgyNXww&ixlib=rb-4.0.3&q=80&w=1080"
            title="Desarrollo Frontend"
            description="Explora las últimas tendencias en desarrollo frontend con React, Next.js y Tailwind CSS para crear interfaces de usuario modernas y responsivas."
            buttonText="Aprender Más"
            onButtonClick={() => alert('¡Aprender más sobre Frontend!')}
          />
          <Card
            imageUrl="https://images.unsplash.com/photo-1522252234503-e356532cafd5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w1NzgyNDF8MHwxfHxjodingfGVufDB8fHx8MTcwMTg4OTgzMHww&ixlib=rb-4.0.3&q=80&w=1080"
            title="Backend con Node.js"
            description="Sumérgete en el mundo del desarrollo backend con Node.js, Express y bases de datos NoSQL para construir APIs robustas y escalables."
            buttonText="Descubrir"
            onButtonClick={() => alert('¡Descubrir Backend!')}
            className="border-4 border-yellow-300"
          />
        </div>
      </section>

      <Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} title="Información Adicional">
        <p className="text-gray-800 mb-4">
          Este es un ejemplo de contenido dinámico dentro del modal. Puedes poner aquí cualquier cosa,
          desde formularios hasta imágenes o más componentes. ¡La flexibilidad es total!
        </p>
        <div className="flex justify-end mt-4">
          <Button variant="secondary" onClick={() => setIsModalOpen(false)}>
            Cerrar
          </Button>
        </div>
      </Modal>
    </main>
  );
}

Aquí, el componente Modal recibe children que es un párrafo y un botón. Esto lo hace increíblemente versátil.

📌 Nota: Como estamos usando `useState`, necesitamos añadir `'use client';` en la parte superior de `app/page.tsx` para marcarlo como un Client Component. El App Router de Next.js renderiza los componentes por defecto como Server Components.

📈 Optimización y Mejores Prácticas

Para sacar el máximo provecho de tus componentes y Tailwind CSS en Next.js:

⚡ PurgeCSS (Automático con Next.js)

Next.js y Tailwind CSS ya vienen configurados para purgar automáticamente las clases CSS no utilizadas en producción. Esto significa que tu CSS final será tan pequeño como sea posible, lo cual es excelente para el rendimiento.

💡 Consejo: Asegúrate de que la propiedad `content` en `tailwind.config.ts` cubra todas las rutas donde usas clases de Tailwind (JSX/TSX, HTML, etc.). Esto es crucial para que el purgado funcione correctamente.

📦 Organiza tus Clases de Tailwind

Cuando un componente tiene muchas clases de Tailwind, el código JSX puede volverse un poco difícil de leer. Puedes usar la librería clsx (o similar) para condicionalmente unir clases o simplemente agruparlas lógicamente.

import clsx from 'clsx'; // npm install clsx

const Button: React.FC<ButtonProps> = ({
  // ...props
}) => {
  const buttonClasses = clsx(
    'font-semibold rounded-md transition-colors duration-200',
    'focus:outline-none focus:ring-2 focus:ring-offset-2',
    {
      // Clases condicionales para variantes
      'bg-blue-600 hover:bg-blue-700 text-white focus:ring-blue-500': variant === 'primary',
      'bg-gray-200 hover:bg-gray-300 text-gray-800 focus:ring-gray-400': variant === 'secondary',
      'bg-red-600 hover:bg-red-700 text-white focus:ring-red-500': variant === 'danger',
    },
    {
      // Clases condicionales para tamaños
      'px-3 py-1 text-sm': size === 'small',
      'px-4 py-2 text-base': size === 'medium',
      'px-6 py-3 text-lg': size === 'large',
    },
    className // Clases personalizadas pasadas como prop
  );

  return (
    <button className={buttonClasses} onClick={onClick}>
      {children}
    </button>
  );
};

Esta forma puede ser más legible y fácil de mantener para componentes con mucha lógica de clases condicionales.

📚 Storybook para Componentes

Para proyectos grandes, considera usar Storybook. Te permite desarrollar, probar y documentar tus componentes de UI de forma aislada. Es excelente para garantizar la calidad y la coherencia del diseño.

¿Cómo integrar Storybook?
  1. Instalar Storybook:
npx storybook@latest init
  1. Configurar para Next.js y Tailwind: A menudo, Storybook necesita algunas configuraciones adicionales para funcionar perfectamente con Next.js (especialmente para next/image y el App Router) y para cargar Tailwind CSS correctamente. Busca la documentación oficial de Storybook para Next.js para los pasos más actualizados.

  2. Escribir Stories: Crea archivos *.stories.tsx junto a tus componentes para definir diferentes estados y variaciones.

    // components/Button/Button.stories.tsx
    import type { Meta, StoryObj } from '@storybook/react';
    import Button from './Button';
    
    const meta: Meta<typeof Button> = {
      title: 'UI/Button',
      component: Button,
      parameters: {
        layout: 'centered',
      },
      tags: ['autodocs'],
      argTypes: {
        onClick: { action: 'clicked' },
      },
    };
    
    export default meta;
    type Story = StoryObj<typeof Button>;
    
    export const Primary: Story = {
      args: {
        variant: 'primary',
        children: 'Primary Button',
      },
    };
    
    export const Secondary: Story = {
      args: {
        variant: 'secondary',
        children: 'Secondary Button',
      },
    };
    // ... otras historias
    

Conclusión ✨

Has llegado al final de este tutorial sobre la construcción de componentes reutilizables y estilizados con Tailwind CSS en Next.js App Router. Hemos cubierto desde la configuración inicial hasta la creación de componentes complejos como Card y Modal, demostrando la potencia de esta combinación.

La clave del éxito en el desarrollo moderno reside en la modularidad, la consistencia y la eficiencia. Al dominar la creación de componentes y el uso de un framework CSS de utilidad como Tailwind, estarás bien equipado para construir aplicaciones robustas, escalables y visualmente atractivas.

¡Sigue practicando, experimentando con diferentes diseños y explorando todas las posibilidades que Next.js y Tailwind CSS tienen para ofrecer! ¡Feliz codificación! 🚀

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!