Route Groups
Los Route Groups (grupos de rutas) son una característica de NextJS que te permite organizar tus rutas en grupos lógicos sin que esos nombres de grupo aparezcan en la URL. Usan paréntesis ()
en el nombre de la carpeta para indicar que son organizacionales y no parte de la ruta.
¿Qué son los Route Groups?
Un Route Group es una carpeta cuyo nombre está envuelto en paréntesis. Esta carpeta NO aparece en la URL final.
app/
├── (marketing)/
│ ├── sobre-nosotros/
│ │ └── page.tsx → /sobre-nosotros
│ └── contacto/
│ └── page.tsx → /contacto
└── (tienda)/
└── productos/
└── page.tsx → /productos
Las carpetas (marketing)
y (tienda)
son Route Groups. No aparecen en las URLs:
/sobre-nosotros
(no/marketing/sobre-nosotros
)/contacto
(no/marketing/contacto
)/productos
(no/tienda/productos
)
Sintaxis básica
Para crear un Route Group, envuelve el nombre de la carpeta en paréntesis:
app/
└── (nombre-del-grupo)/
└── ruta/
└── page.tsx
Reglas:
- El nombre entre paréntesis NO aparece en la URL
- Puedes anidar Route Groups
- Puedes tener archivos especiales (
layout.tsx
,loading.tsx
, etc.) en el grupo - Los Route Groups son ignorados por el sistema de routing
Nombre del grupo
El nombre dentro de los paréntesis es solo para tu organización. Puede ser cualquier cosa descriptiva: (admin)
, (auth)
, (publico)
, (v1)
, etc.
Casos de uso principales
1. Organización lógica
Agrupa rutas relacionadas sin afectar las URLs:
app/
├── (marketing)/
│ ├── home/
│ │ └── page.tsx → /home
│ ├── sobre-nosotros/
│ │ └── page.tsx → /sobre-nosotros
│ ├── blog/
│ │ └── page.tsx → /blog
│ └── contacto/
│ └── page.tsx → /contacto
│
└── (app)/
├── dashboard/
│ └── page.tsx → /dashboard
├── perfil/
│ └── page.tsx → /perfil
└── configuracion/
└── page.tsx → /configuracion
Beneficios:
- Tu código está organizado por función
- Las URLs permanecen limpias
- Es fácil encontrar archivos relacionados
2. Layouts diferentes por sección
El caso de uso más común: aplicar layouts diferentes a diferentes partes de tu aplicación.
app/
├── (sitio-publico)/
│ ├── layout.tsx
│ ├── home/
│ │ └── page.tsx → /home
│ └── contacto/
│ └── page.tsx → /contacto
│
└── (dashboard)/
├── layout.tsx
├── analytics/
│ └── page.tsx → /analytics
└── reportes/
└── page.tsx → /reportes
Layout del sitio público:
// app/(sitio-publico)/layout.tsx
export default function SitioPublicoLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<div>
<header>
<nav>Navegación pública</nav>
</header>
<main>{children}</main>
<footer>
<p>© 2025 Mi Sitio</p>
</footer>
</div>
)
}
Layout del dashboard:
// app/(dashboard)/layout.tsx
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<div className="dashboard">
<aside className="sidebar">
<nav>Menú del dashboard</nav>
</aside>
<main>{children}</main>
</div>
)
}
Cada grupo tiene su propio layout, pero las URLs permanecen simples.
3. Múltiples root layouts
Por defecto, solo puedes tener un Root Layout en app/layout.tsx
. Pero con Route Groups, puedes tener múltiples "root layouts" para diferentes secciones.
app/
├── (sitio)/
│ ├── layout.tsx
│ ├── home/
│ └── about/
│
└── (app)/
├── layout.tsx
├── dashboard/
└── settings/
Importante
Cuando usas múltiples root layouts con Route Groups, cada layout DEBE incluir las etiquetas html y body. No puedes tener un layout global que los incluya porque cada grupo es independiente.
Ejemplo con múltiples root layouts:
// app/(sitio)/layout.tsx
export default function SitioLayout({
children
}: {
children: React.ReactNode
}) {
return (
<html lang="es">
<body className="sitio-publico">
<header>
<h1>Mi Sitio</h1>
</header>
{children}
<footer>
<p>Copyright 2025</p>
</footer>
</body>
</html>
)
}
// app/(app)/layout.tsx
export default function AppLayout({
children
}: {
children: React.ReactNode
}) {
return (
<html lang="es">
<body className="aplicacion">
<div className="app-container">
<aside>
<nav>Sidebar de la aplicación</nav>
</aside>
<main>{children}</main>
</div>
</body>
</html>
)
}
Cada sección tiene su propio html y body, lo que permite estilos y estructuras completamente diferentes.
Ejemplo completo - E-commerce
Estructura típica de una tienda online con Route Groups:
app/
│
├── (marketing)/
│ ├── layout.tsx
│ ├── page.tsx → /
│ ├── sobre-nosotros/
│ │ └── page.tsx → /sobre-nosotros
│ ├── contacto/
│ │ └── page.tsx → /contacto
│ └── blog/
│ ├── page.tsx → /blog
│ └── [slug]/
│ └── page.tsx → /blog/mi-articulo
│
├── (tienda)/
│ ├── layout.tsx
│ ├── productos/
│ │ ├── page.tsx → /productos
│ │ └── [slug]/
│ │ └── page.tsx → /productos/camisa-azul
│ ├── categorias/
│ │ └── [slug]/
│ │ └── page.tsx → /categorias/ropa
│ └── carrito/
│ └── page.tsx → /carrito
│
├── (cuenta)/
│ ├── layout.tsx
│ ├── perfil/
│ │ └── page.tsx → /perfil
│ ├── pedidos/
│ │ ├── page.tsx → /pedidos
│ │ └── [id]/
│ │ └── page.tsx → /pedidos/123
│ └── configuracion/
│ └── page.tsx → /configuracion
│
└── (auth)/
├── layout.tsx
├── login/
│ └── page.tsx → /login
└── registro/
└── page.tsx → /registro
Layouts específicos por grupo
Marketing - Con header y footer completo:
// app/(marketing)/layout.tsx
export default function MarketingLayout({ children }) {
return (
<div className="marketing-layout">
<header className="header-principal">
<nav>
<a href="/">Inicio</a>
<a href="/sobre-nosotros">Sobre Nosotros</a>
<a href="/blog">Blog</a>
<a href="/productos">Tienda</a>
</nav>
</header>
<main>{children}</main>
<footer>
<div>
<h4>Contacto</h4>
<p>info@tienda.com</p>
</div>
<div>
<h4>Síguenos</h4>
<p>Facebook | Instagram</p>
</div>
</footer>
</div>
)
}
Tienda - Con sidebar de filtros:
// app/(tienda)/layout.tsx
export default function TiendaLayout({ children }) {
return (
<div className="tienda-layout">
<aside className="filtros">
<h3>Filtros</h3>
<div>
<h4>Categoría</h4>
<ul>
<li>Ropa</li>
<li>Zapatos</li>
</ul>
</div>
<div>
<h4>Precio</h4>
<input type="range" />
</div>
</aside>
<main className="productos-area">
{children}
</main>
</div>
)
}
Cuenta - Con menú lateral:
// app/(cuenta)/layout.tsx
export default function CuentaLayout({ children }) {
return (
<div className="cuenta-layout">
<aside className="menu-cuenta">
<nav>
<a href="/perfil">Mi Perfil</a>
<a href="/pedidos">Mis Pedidos</a>
<a href="/configuracion">Configuración</a>
</nav>
</aside>
<main className="contenido-cuenta">
{children}
</main>
</div>
)
}
Auth - Layout minimalista:
// app/(auth)/layout.tsx
export default function AuthLayout({ children }) {
return (
<div className="auth-layout">
<div className="auth-container">
<div className="logo">Mi Tienda</div>
{children}
</div>
</div>
)
}
Cada sección tiene un layout completamente diferente, pero las URLs permanecen limpias sin nombres de grupo.
Route Groups anidados
Puedes anidar Route Groups dentro de otros:
app/
└── (dashboard)/
├── layout.tsx
├── (analytics)/
│ ├── ventas/
│ │ └── page.tsx → /ventas
│ └── usuarios/
│ └── page.tsx → /usuarios
│
└── (settings)/
├── perfil/
│ └── page.tsx → /perfil
└── seguridad/
└── page.tsx → /seguridad
Los grupos anidados tampoco aparecen en la URL. Solo sirven para organización adicional.
Route Groups con rutas comunes
Puedes tener rutas con el mismo nombre en diferentes grupos:
app/
├── (admin)/
│ └── dashboard/
│ └── page.tsx → /dashboard
│
└── (usuario)/
└── dashboard/
└── page.tsx → /dashboard
Conflicto de rutas
Esto causará un ERROR. No puedes tener dos páginas con la misma URL, aunque estén en diferentes Route Groups. NextJS no sabrá cuál usar.
Para resolver esto, usa rutas diferentes o parámetros dinámicos.
Solución - Rutas diferentes:
app/
├── (admin)/
│ └── admin-dashboard/
│ └── page.tsx → /admin-dashboard
│
└── (usuario)/
└── dashboard/
└── page.tsx → /dashboard
Solución - Con segmentos dinámicos:
app/
└── dashboard/
└── [tipo]/
└── page.tsx → /dashboard/admin
→ /dashboard/usuario
Combinando con otras características
Route Groups + Dynamic Routes
app/
└── (blog)/
├── layout.tsx
├── articulos/
│ └── page.tsx → /articulos
└── [categoria]/
└── [slug]/
└── page.tsx → /tecnologia/mi-articulo
El grupo (blog)
aplica un layout, mientras que las rutas dinámicas funcionan normalmente.
Route Groups + Parallel Routes
Los Route Groups funcionan bien con parallel routes (rutas paralelas):
app/
└── (dashboard)/
├── @sidebar/
│ └── page.tsx
├── @contenido/
│ └── page.tsx
└── layout.tsx
El grupo organiza, mientras las parallel routes manejan el rendering paralelo.
Archivos especiales en Route Groups
Puedes tener archivos especiales dentro de Route Groups:
app/
└── (dashboard)/
├── layout.tsx
├── loading.tsx
├── error.tsx
├── not-found.tsx
└── template.tsx
Estos archivos se aplican a todas las rutas dentro del grupo.
Ejemplo con loading y error
// app/(tienda)/layout.tsx
export default function TiendaLayout({ children }) {
return (
<div>
<h1>Mi Tienda</h1>
{children}
</div>
)
}
// app/(tienda)/loading.tsx
export default function TiendaLoading() {
return <div>Cargando productos...</div>
}
// app/(tienda)/error.tsx
'use client'
export default function TiendaError({ error, reset }) {
return (
<div>
<h2>Error en la tienda</h2>
<p>{error.message}</p>
<button onClick={reset}>Intentar de nuevo</button>
</div>
)
}
Todas las páginas dentro de (tienda)/
usan estos archivos automáticamente.
Metadata en Route Groups
Los Route Groups pueden tener su propia metadata:
// app/(marketing)/layout.tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: {
template: '%s | Mi Tienda',
default: 'Mi Tienda',
},
description: 'La mejor tienda online',
}
export default function MarketingLayout({ children }) {
return <div>{children}</div>
}
Las páginas dentro del grupo heredan y extienden esta metadata.
Cuándo usar Route Groups
Usa Route Groups cuando:
- Necesitas layouts diferentes para diferentes secciones
- Quieres organizar rutas lógicamente sin afectar URLs
- Necesitas aplicar middleware solo a ciertas rutas
- Quieres separar código de admin vs usuario
- Necesitas diferentes estilos globales por sección
No uses Route Groups cuando:
- El grupo debería aparecer en la URL (usa carpetas normales)
- Solo tienes unas pocas rutas
- No necesitas layouts diferentes
- Complica más de lo que ayuda
Mejores prácticas
1. Nombres descriptivos
// Nombres claros
(marketing)/
(tienda)/
(cuenta)/
(auth)/
(admin)/
// Nombres genéricos (evita)
(grupo1)/
(seccion)/
(otro)/
2. No anides demasiado
// Demasiados niveles
app/(a)/(b)/(c)/(d)/ruta/
// Mantén simple
app/(seccion)/ruta/
3. Consistencia en la estructura
// Estructura consistente
app/
├── (publico)/
│ ├── layout.tsx
│ ├── home/
│ └── about/
└── (privado)/
├── layout.tsx
├── dashboard/
└── settings/
4. Documenta tu estructura
Agrega un README en tu proyecto explicando los grupos:
# Estructura de Route Groups
## (marketing)
Páginas públicas de marketing con header/footer completo.
## (tienda)
Área de productos con sidebar de filtros.
## (cuenta)
Área privada del usuario con menú lateral.
## (auth)
Páginas de login/registro con layout minimalista.
5. Un propósito por grupo
// Cada grupo tiene un propósito claro
(marketing)/
(tienda)/
(admin)/
// Grupos sin propósito claro (evita)
(varios)/
(otros)/
Errores comunes
1. Esperar que el grupo aparezca en la URL
app/(admin)/dashboard/page.tsx
// Incorrecto - pensando que la URL será:
/admin/dashboard
// Correcto - la URL real es:
/dashboard
El nombre del grupo NO aparece en la URL.
2. Rutas duplicadas
app/
├── (a)/
│ └── page.tsx → /
└── (b)/
└── page.tsx → /
ERROR: Dos páginas intentan usar la misma URL.
3. Olvidar html y body con múltiples root layouts
Cuando tienes múltiples root layouts con Route Groups, cada uno necesita las etiquetas html y body completas:
// Incorrecto
export default function Layout({ children }) {
return <div>{children}</div>
}
// Correcto
export default function Layout({ children }) {
return (
<html lang="es">
<body>
{children}
</body>
</html>
)
}
4. Sobre-organizar
// Demasiados grupos para pocas rutas
app/
├── (grupo1)/
│ └── page1/
├── (grupo2)/
│ └── page2/
└── (grupo3)/
└── page3/
// Simplifica si no hay beneficio
app/
├── page1/
├── page2/
└── page3/
Solo usa grupos cuando realmente los necesites.
Resumen
Route Groups:
- Nombres de carpeta entre paréntesis:
(nombre)
- NO aparecen en la URL
- Útiles para organización lógica
- Permiten layouts diferentes por sección
- Pueden tener sus propios archivos especiales
Sintaxis:
(nombre-del-grupo)/
Casos de uso principales:
- Organización sin afectar URLs
- Layouts diferentes por sección
- Múltiples root layouts
Reglas importantes:
- Los nombres de grupo son solo para organización
- No pueden crear rutas duplicadas
- Con múltiples root layouts, cada uno necesita html y body
- Funcionan con todas las demás características de routing
Cuándo usar:
- Necesitas layouts diferentes
- Quieres organizar código lógicamente
- Separar secciones públicas/privadas
- Sin afectar las URLs