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:

  1. Organización sin afectar URLs
  2. Layouts diferentes por sección
  3. 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