
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ñal | Acción |
|---|---|
| "¿Dónde va este archivo?" — todo el mundo pregunta | Clarifica las reglas, documéntalas |
| Archivos de una feature repartidos en 3+ carpetas | Agrupa por feature |
| Eliminar una feature requiere buscar en todo el repositorio | Reorganiza por feature |
| Los nuevos devs tardan >1 hora en encontrar cosas | Demasiado 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.tspara definir su API pública - Escribe las reglas — incluso un README de 5 líneas en
/featuresayuda 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.