Definiendo Rutas
En NextJS 15, las rutas se definen mediante la estructura de carpetas dentro del directorio app/. Esta guía explica en detalle cómo crear rutas correctamente, qué convenciones seguir y cómo organizar tu aplicación.
Anatomía de una ruta
Una ruta en NextJS está compuesta por segmentos de ruta. Cada segmento corresponde a una carpeta en tu estructura de archivos:
app/tienda/productos/[id]/page.tsxEsta estructura se divide en:
| Segmento | Tipo | URL resultante |
|---|---|---|
tienda | Estático | /tienda |
productos | Estático | /tienda/productos |
[id] | Dinámico | /tienda/productos/123 |
Ruta completa: /tienda/productos/123
Cada carpeta es un segmento de la URL final.
Convenciones de nomenclatura
Nombres de carpetas
Las carpetas que definen tus rutas deben seguir estas reglas:
Caracteres permitidos:
- Letras minúsculas:
a-z - Números:
0-9 - Guion medio:
mi-ruta - Guion bajo (underscore):
mi_ruta
Evita usar:
- Espacios:
mi ruta - Mayúsculas:
MiRuta(funcionan pero no es recomendado) - Caracteres especiales:
mi@ruta,mi#ruta - Puntos:
mi.ruta(puede causar problemas)
app/
├── productos/ Correcto
├── mis-productos/ Correcto (preferido)
├── mis_productos/ Funciona pero no recomendado
├── Productos/ △ Funciona pero evítalo
├── mis productos/ x No funcionará
└── mis.productos/ x Puede causar problemasConvención recomendada
Usa siempre minúsculas con guiones medios para separar palabras: carrito-de-compras, historial-pedidos, datos-usuario. Esto es más legible y es el estándar en la comunidad.
Sensibilidad a mayúsculas
En desarrollo local (Windows, macOS), las rutas NO son sensibles a mayúsculas. Pero en producción (servidores Linux), SÍ lo son:
Desarrollo:
/Productos === /productos === /PRODUCTOS
Producción:
/Productos ≠ /productos ≠ /PRODUCTOSSiempre usa minúsculas para evitar problemas al deployar.
Segmentos de ruta
Segmentos estáticos
Son carpetas con nombres fijos que definen rutas exactas:
app/
├── tienda/
│ └── page.tsx → /tienda
├── contacto/
│ └── page.tsx → /contacto
└── sobre-nosotros/
└── page.tsx → /sobre-nosotrosSegmentos dinámicos
Usan corchetes [] para crear rutas que aceptan valores variables:
app/
└── productos/
└── [id]/
└── page.tsx → /productos/123
→ /productos/abc
→ /productos/cualquier-valorEl valor entre corchetes se convierte en un parámetro:
// app/productos/[id]/page.tsx
export default function ProductoPage({
params,
}: {
params: { id: string }
}) {
// params.id contiene el valor de la URL
return <h1>Producto ID: {params.id}</h1>
}Los segmentos dinámicos se explican a fondo en Dynamic Routes. Esta sección solo cubre lo básico para entender cómo se definen.
Segmentos catch-all (atrapar todo)
Usan corchetes dobles [[...]] para capturar múltiples segmentos:
app/
└── docs/
└── [...slug]/
└── page.tsx → /docs/a
→ /docs/a/b
→ /docs/a/b/c// app/docs/[...slug]/page.tsx
export default function DocsPage({
params,
}: {
params: { slug: string[] }
}) {
// params.slug es un array
// /docs/guias/nextjs → slug = ['guias', 'nextjs']
return <h1>Documentación: {params.slug.join('/')}</h1>
}Variante opcional: [[...slug]] con corchetes dobles también captura la ruta base:
app/
└── tienda/
└── [[...categoria]]/
└── page.tsx → /tienda (slug = undefined)
→ /tienda/ropa (slug = ['ropa'])
→ /tienda/ropa/hombre (slug = ['ropa', 'hombre'])Archivos que definen rutas
Solo ciertos archivos hacen que una carpeta sea accesible como ruta:
page.tsx - Hace la ruta pública
Sin page.tsx, la carpeta NO es accesible:
app/
├── productos/ ← /productos no existe (no hay page.tsx)
│ └── [id]/
│ └── page.tsx ← /productos/123 sí existe
└── tienda/
└── page.tsx ← /tienda sí existeResultado:
/tienda→ funciona/productos→ 404 (no hay page.tsx)/productos/123→ funciona
Error común
Crear una carpeta sin page.tsx no crea una ruta. Si accedes a esa URL, obtendrás un error 404.
Archivos que NO crean rutas
Estos archivos tienen funciones especiales pero no hacen que la carpeta sea accesible:
app/
└── dashboard/
├── layout.tsx ← Proporciona UI compartida
├── loading.tsx ← Muestra estado de carga
├── error.tsx ← Maneja errores
├── not-found.tsx ← Página 404 personalizada
└── template.tsx ← Similar a layout pero no persistenteSin un page.tsx, /dashboard no es accesible aunque tenga estos archivos.
Colocation - Archivos junto a rutas
Puedes colocar archivos que NO son rutas dentro de app/:
app/
└── productos/
├── page.tsx ← Ruta: /productos
├── components/
│ └── ProductCard.tsx ← Componente (no es ruta)
├── utils/
│ └── formatPrice.ts ← Utilidad (no es ruta)
└── styles.module.css ← Estilos (no es ruta)NextJS solo trata como rutas las carpetas que contienen archivos especiales de routing (page.tsx, etc.).
Esta característica se llama colocation (colocación en español) y te permite organizar archivos relacionados cerca de las rutas que los usan.
Ejemplo real - E-commerce
app/
└── tienda/
├── page.tsx ← /tienda
├── layout.tsx ← Layout para toda la tienda
├── components/
│ ├── ProductGrid.tsx ← Componente compartido
│ ├── FilterSidebar.tsx ← Componente compartido
│ └── SortDropdown.tsx ← Componente compartido
├── productos/
│ ├── page.tsx ← /tienda/productos
│ ├── [slug]/
│ │ ├── page.tsx ← /tienda/productos/camisa-azul
│ │ ├── components/
│ │ │ ├── ProductImages.tsx ← Componente específico
│ │ │ ├── AddToCart.tsx ← Componente específico
│ │ │ └── RelatedProducts.tsx ← Componente específico
│ │ └── actions.ts ← Server Actions
│ └── actions/
│ └── getProducts.ts ← Data fetching
└── carrito/
├── page.tsx ← /tienda/carrito
└── components/
└── CartItem.tsx ← Componente específicoSolo las carpetas con page.tsx crean rutas públicas. Todo lo demás es organización interna.
Carpetas privadas
Usa guion bajo (underscore, el símbolo _) al inicio del nombre para crear carpetas privadas que NextJS ignora completamente:
app/
├── _lib/ ← Carpeta privada
│ ├── database.ts
│ └── utils.ts
├── _components/ ← Carpeta privada
│ ├── Header.tsx
│ └── Footer.tsx
└── productos/
└── page.tsx ← /productosCaracterísticas de carpetas privadas:
- No son accesibles como rutas (incluso si tienen
page.tsx) - Útiles para organizar código compartido
- No afectan el sistema de routing
- Puedes anidarlas dentro de rutas normales
app/
└── dashboard/
├── page.tsx ← /dashboard
├── _components/ ← Privada (componentes internos)
│ └── Sidebar.tsx
└── analytics/
├── page.tsx ← /dashboard/analytics
└── _utils/ ← Privada (utilidades internas)
└── calculations.ts¿Cuándo usar carpetas privadas?
Úsalas cuando quieras organizar archivos dentro de app/ pero no quieres arriesgarte a que NextJS los trate como rutas en el futuro. Es una forma explícita de decir "esto no es una ruta".
Route Groups - Organización sin URL
Los Route Groups usan paréntesis () para organizar rutas sin afectar la URL:
app/
├── (marketing)/
│ ├── layout.tsx ← Layout solo para marketing
│ ├── sobre-nosotros/
│ │ └── page.tsx → /sobre-nosotros
│ ├── contacto/
│ │ └── page.tsx → /contacto
│ └── blog/
│ └── page.tsx → /blog
└── (tienda)/
├── layout.tsx ← Layout solo para tienda
├── productos/
│ └── page.tsx → /productos
└── carrito/
└── page.tsx → /carritoLos nombres (marketing) y (tienda) NO aparecen en la URL. Son solo para organización y para aplicar layouts diferentes.
Casos de uso:
- Layouts diferentes por sección:
app/
├── (auth)/ ← Layout sin sidebar
│ ├── layout.tsx
│ ├── login/
│ └── register/
└── (dashboard)/ ← Layout con sidebar
├── layout.tsx
├── home/
└── settings/- Organización lógica:
app/
├── (publico)/
│ ├── home/
│ └── about/
└── (privado)/
├── dashboard/
└── profile/Los Route Groups se explican en detalle en Route Groups. Aquí solo cubrimos cómo afectan la definición de rutas.
Jerarquía de archivos especiales
NextJS busca estos archivos en cada segmento de ruta (en este orden):
app/segmento/
├── layout.tsx 1. Envuelve todo (persistente)
├── template.tsx 2. Similar a layout (no persistente)
├── error.tsx 3. Maneja errores
├── loading.tsx 4. Muestra mientras carga
├── not-found.tsx 5. 404 personalizado
└── page.tsx 6. Contenido de la rutaCómo se anidan:
layout.tsx
└─ template.tsx
└─ error.tsx
└─ loading.tsx (mientras carga)
└─ page.tsxSi hay un error, error.tsx lo captura. Si la ruta no existe, not-found.tsx se muestra.
Casos especiales y edge cases
Ruta raíz vs otras rutas
app/
├── page.tsx ← / (raíz)
└── home/
└── page.tsx ← /home (diferente)La raíz / y /home son rutas distintas. No redirigen automáticamente una a la otra.
Múltiples page.tsx en la misma ruta
app/
└── productos/
├── page.tsx Correcto
└── page.js x Error: conflictoSolo puede haber UN archivo page por carpeta (ya sea .tsx, .ts, .jsx, o .js).
Carpetas vacías
app/
└── productos/ ← Carpeta vacíaLas carpetas vacías se ignoran. No crean rutas ni causan errores.
Archivos sueltos en app/
app/
├── page.tsx Ruta: /
├── utils.ts Ignorado (no es archivo especial)
└── constants.ts Ignorado (no es archivo especial)Los archivos que no son page.tsx, layout.tsx, etc., se ignoran y no afectan el routing.
Segmentos con puntos
app/
└── api.v1/ △ Funciona pero evítalo
└── page.tsx → /api.v1Técnicamente funciona pero puede causar confusión. Mejor usa guiones: api-v1.
Mejores prácticas
1. Nomenclatura consistente
Buenos nombres:
- productos
- detalle-producto
- historial-pedidos
- configuracion-usuario
x Evita:
- Productos (mayúscula)
- detalle_producto (underscore)
- historial.pedidos (punto)
- configuración-usuario (tilde)2. Estructura plana cuando sea posible
x Demasiado anidado:
app/tienda/categoria/productos/item/detalle/page.tsx
→ /tienda/categoria/productos/item/detalle
Más plano:
app/tienda/producto/[id]/page.tsx
→ /tienda/producto/123URLs más cortas son mejores para SEO y UX.
3. Agrupa archivos relacionados
Organizado:
app/
└── productos/
├── page.tsx
├── layout.tsx
├── loading.tsx
├── error.tsx
├── components/
├── utils/
└── [id]/
└── page.tsx
x Desorganizado:
app/
├── productos/
│ └── page.tsx
└── producto-details/
└── page.tsx ← Debería estar en productos/[id]4. Usa Route Groups para layouts diferentes
app/
├── (marketing)/
│ ├── layout.tsx ← Layout con header/footer
│ └── home/
├── (app)/
│ ├── layout.tsx ← Layout con sidebar
│ └── dashboard/
└── (auth)/
├── layout.tsx ← Layout simple
└── login/5. Carpetas privadas para código compartido
app/
├── _lib/ ← Funciones compartidas
├── _components/ ← Componentes globales
├── _hooks/ ← Custom hooks
└── dashboard/
└── page.tsx6. Nombres descriptivos
Descriptivos:
- detalle-producto
- historial-compras
- configuracion-perfil
x Genéricos:
- item
- details
- pageEjemplo completo - E-commerce
Estructura completa de una aplicación de e-commerce:
app/
├── layout.tsx ← Layout global
├── page.tsx ← / (home)
├── not-found.tsx ← 404 global
│
├── _lib/ ← Carpeta privada
│ ├── db.ts
│ └── utils.ts
│
├── _components/ ← Componentes globales
│ ├── Header.tsx
│ └── Footer.tsx
│
├── (marketing)/ ← Route group
│ ├── layout.tsx ← Layout con header/footer
│ ├── sobre-nosotros/
│ │ └── page.tsx → /sobre-nosotros
│ ├── contacto/
│ │ └── page.tsx → /contacto
│ └── blog/
│ ├── page.tsx → /blog
│ └── [slug]/
│ └── page.tsx → /blog/mi-articulo
│
├── (tienda)/ ← Route group
│ ├── layout.tsx ← Layout de tienda
│ ├── productos/
│ │ ├── page.tsx → /productos
│ │ ├── loading.tsx
│ │ ├── components/
│ │ │ ├── ProductCard.tsx
│ │ │ └── FilterBar.tsx
│ │ └── [slug]/
│ │ ├── page.tsx → /productos/camisa-azul
│ │ ├── loading.tsx
│ │ ├── error.tsx
│ │ └── components/
│ │ ├── ProductGallery.tsx
│ │ └── AddToCart.tsx
│ │
│ ├── categorias/
│ │ └── [slug]/
│ │ └── page.tsx → /categorias/ropa
│ │
│ ├── carrito/
│ │ ├── page.tsx → /carrito
│ │ └── components/
│ │ └── CartItem.tsx
│ │
│ └── checkout/
│ ├── page.tsx → /checkout
│ ├── layout.tsx
│ └── confirmacion/
│ └── page.tsx → /checkout/confirmacion
│
└── (cuenta)/ ← Route group
├── layout.tsx ← Layout de cuenta
├── perfil/
│ └── page.tsx → /perfil
├── pedidos/
│ ├── page.tsx → /pedidos
│ └── [id]/
│ └── page.tsx → /pedidos/123
└── configuracion/
└── page.tsx → /configuracionEstructura de URLs resultante:
Rutas públicas:
/
/sobre-nosotros
/contacto
/blog
/blog/mi-articulo
Rutas de tienda:
/productos
/productos/camisa-azul
/categorias/ropa
/carrito
/checkout
/checkout/confirmacion
Rutas de cuenta:
/perfil
/pedidos
/pedidos/123
/configuracionLos Route Groups (marketing), (tienda), y (cuenta) no aparecen en las URLs pero permiten aplicar layouts diferentes a cada sección.
Resumen
Puntos clave para definir rutas:
- Las carpetas dentro de
app/definen segmentos de ruta - Solo las carpetas con
page.tsxson accesibles como rutas - Usa minúsculas y guiones medios para nombres de carpetas
- Los corchetes
[]crean segmentos dinámicos - Los paréntesis
()crean Route Groups que no afectan la URL - El guion bajo
_crea carpetas privadas que NextJS ignora - Puedes colocar archivos no relacionados con routing junto a tus rutas
- Los archivos especiales (
layout,loading,error) no crean rutas por sí mismos
Convenciones importantes:
- Nombres de archivo:
page.tsx,layout.tsx,loading.tsx,error.tsx,not-found.tsx - Un solo
page.tsxpor carpeta - Las rutas son case-sensitive en producción
- URLs más cortas son mejores