Disponible para freelance¡Contáctame para que pueda ayudar a que tu negocio crezca o convertir tu idea en realidad!

Estoy interesado
Estructura de Carpetas en el Frontend Que Escala

Estructura de Carpetas en el Frontend Que Escala

Todo proyecto empieza limpio. Seis meses después, components/ tiene 80 archivos, nadie sabe dónde está nada, y el onboarding tarda una semana.

La estructura de carpetas no es el problema. La ausencia de reglas lo es.


Los Dos Enfoques Comunes

Por tipo (común al inicio)

components/
  Button.tsx
  Modal.tsx
  ProductCard.tsx
  UserProfile.tsx
hooks/
  useCart.ts
  useAuth.ts
  useProducts.ts
utils/
  formatDate.ts
  formatPrice.ts

Parece organizado. Pero a medida que la app crece, components/ se convierte en un vertedero. Nada está agrupado por lo que pertenece junto.


Por funcionalidad (lo que realmente escala)

features/
  cart/
    components/
    hooks/
    utils/
    index.ts
  auth/
    components/
    hooks/
    utils/
    index.ts
  products/
    components/
    hooks/
    utils/
    index.ts

Cada funcionalidad es dueña de su código. Puedes leer, cambiar o eliminar una feature sin buscar por todo el repositorio.


Una Estructura Práctica para Next.js App Router

app/
  [locale]/
    (marketing)/
      page.tsx
    (app)/
      dashboard/
        page.tsx
      settings/
        page.tsx
    layout.tsx

components/
  ui/           # genéricos, reutilizables (Button, Input, Modal)
  layout/       # estructurales (Header, Footer, Sidebar)

features/
  auth/
    components/
      LoginForm.tsx
      AuthGuard.tsx
    hooks/
      useAuth.ts
    actions/
      login.ts
    index.ts
  cart/
    components/
      CartDrawer.tsx
      CartItem.tsx
    hooks/
      useCart.ts
    store/
      cart.store.ts
    index.ts

lib/
  db.ts
  flags.ts
  analytics.ts

hooks/           # solo hooks verdaderamente globales
utils/           # solo utilidades verdaderamente globales
types/           # tipos TypeScript compartidos

El Principio de Colocalización

Mantén el código cerca de donde se usa.

Si un hook solo se usa dentro de CartDrawer.tsx, no pertenece a /hooks. Pertenece junto al componente.

features/cart/
  components/
    CartDrawer.tsx
    CartDrawer.hooks.ts    # solo usado aquí
  hooks/
    useCart.ts             # usado en toda la feature de cart

Sube en el árbol de carpetas solo cuando algo es compartido entre features. No muevas archivos "por si acaso".


El Patrón de Barrel con index.ts

Usa index.ts para definir lo que una feature expone públicamente.

// features/cart/index.ts
export { CartDrawer } from './components/CartDrawer'
export { useCart } from './hooks/useCart'
export type { CartItem } from './types'

Luego importa de forma limpia desde otras features:

import { CartDrawer, useCart } from '@/features/cart'

Esta es la API pública de tu feature. Los archivos internos se mantienen internos.


Cuándo los Barrel Files Fallan

Los barrel files mejoran los imports — pero tienen un coste.

// ❌ Cada archivo de la app se incluye en el bundle cuando esto se importa
export * from './Button'
export * from './Modal'
export * from './ProductCard'
// ...50 exports más

El tree-shaking falla con barrels grandes — y tu bundle paga las consecuencias.

Regla: Usa barrel files a nivel de feature (no en components/ui/). Mantén los barrels de UI pequeños y explícitos.


Route Groups en Next.js App Router

Usa route groups (nombre-grupo) para separar responsabilidades sin afectar la URL.

app/
  [locale]/
    (marketing)/
      page.tsx          → /es
      about/
        page.tsx        → /es/about
    (app)/
      layout.tsx        → layout autenticado compartido
      dashboard/
        page.tsx        → /es/dashboard
      settings/
        page.tsx        → /es/settings

Las páginas de marketing y las páginas de la app comparten el parámetro de locale pero tienen layouts diferentes. Los route groups los separan de forma limpia.


Errores Comunes

1. Una carpeta components/ gigante

components/
  LoginButton.tsx
  ProductCard.tsx
  CartItem.tsx
  DashboardHeader.tsx
  AdminTable.tsx
  ... 70 archivos más

Sin agrupación = sin estructura. Extrae a features.


2. hooks/ global para hooks específicos de una feature

// ❌ hooks/useCartDiscount.ts — solo usado en cart
// ✅ features/cart/hooks/useCartDiscount.ts

3. Anidamiento demasiado profundo

features/
  checkout/
    components/
      steps/
        payment/
          forms/
            fields/
              CreditCardField.tsx

Más allá de 4 niveles, la navegación se vuelve dolorosa. Aplana cuando el anidamiento no añade significado.


4. Importar directamente entre features

// ❌ Acopla features directamente
import { CartItem } from '../cart/components/CartItem'
// ✅ Importa a través de la API pública
import { CartItem } from '@/features/cart'

Esto hace seguro refactorizar una feature sin romper otras.


Cuándo Refactorizar la Estructura

SeñalAcción
"¿Dónde va este archivo?" — todo el mundo preguntaClarifica las reglas, documéntalas
Archivos de una feature repartidos en 3+ carpetasAgrupa por feature
Eliminar una feature requiere buscar en todo el repositorioReorganiza por feature
Los nuevos devs tardan >1 hora en encontrar cosasDemasiado anidamiento o sin convenciones

Qué Hacer Ahora

  • Audita tu estructura actual: cuenta archivos por carpeta, identifica los vertederos
  • Elige una feature y muévela a features/tu-feature/
  • Añade un index.ts para definir su API pública
  • Escribe las reglas — incluso un README de 5 líneas en /features ayuda en el onboarding

La estructura no se trata de perfección. Se trata de hacer obvia la siguiente decisión.

Cuando llega un nuevo archivo, la carpeta correcta debe ser clara sin necesidad de una discusión en equipo. Ese es el objetivo.