En el enrutamiento basado en archivos de App Router, la estructura de carpetas se proyecta directamente sobre las URLs públicas. Sin embargo, en arquitecturas empresariales complejas, las necesidades de organización del repositorio colisionan a menudo con la estructura requerida para las URLs:
/login frente a un entorno de trabajo con barra lateral en /dashboard).Next.js App Router solventa esto mediante los Route Groups (grupos de rutas). Envolver el nombre de una carpeta entre paréntesis indica a Turbopack que dicho segmento existe únicamente para estructuración interna y aplicación de layouts, omitiéndolo de la ruta URL resultante.
Cuando el compilador de Next.js procesa la carpeta app, los nombres delimitados por paréntesis (folderName) actúan como fronteras organizativas en el sistema de archivos, pero son eliminados al construir la tabla de rutas:
app/(marketing)/about/page.tsx se compila en /about.app/(dashboard)/analytics/page.tsx se compila en /analytics.app/(shop)/checkout/page.tsx se compila en /checkout.A la vez, el compilador conserva el grupo en su Route Segment Tree ID. Esto permite que cada grupo disponga de sus propios layout.tsx, loading.tsx y error.tsx independientes.
Turbopack escanea la jerarquía de app/ e identifica carpetas entre paréntesis como (marketing) o (dashboard).
Los identificadores entre paréntesis se excluyen de la tabla de URLs: app/(marketing)/pricing/page.tsx se registra como /pricing.
La página se enlaza con el layout de su grupo: pricing/page.tsx queda envuelto por (marketing)/layout.tsx.
Al navegar, Next.js comprueba el ancestro raíz: si ambas páginas comparten Root Layout la transición es suave (SPA); de lo contrario, se recarga la página.
El servidor emite un Flight stream con los componentes de la página y layout del grupo destino sin dependencias no requeridas.
En un proyecto Next.js existe el archivo app/(auth)/admin/login/page.tsx. ¿Bajo qué URL pública se accederá a esta página en el navegador?
Dentro de una misma aplicación, diferentes secciones pueden requerir disposiciones distintas. Al crear grupos, se aíslan los layouts sin alterar la URL:
app/
├── (marketing)/
│ ├── layout.tsx # Layout público (Header, Banner, Footer)
│ ├── page.tsx # Ruta: / (Inicio de Marketing)
│ ├── about/page.tsx # Ruta: /about
│ └── pricing/page.tsx # Ruta: /pricing
└── (dashboard)/
├── layout.tsx # Layout privado (Sidebar, Menú de usuario)
├── dashboard/page.tsx # Ruta: /dashboard
└── settings/page.tsx # Ruta: /settingsNext.js permite suprimir el archivo raíz app/layout.tsx y declarar Root Layouts independientes dentro de cada grupo:
export default function MarketingRootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="es">
<body className="marketing-theme">
{/* HTML estático limpio sin proveedores de cliente pesados */}
<header className="marketing-header">Encabezado Marketing</header>
{children}
</body>
</html>
);
}import { AuthProvider } from '@/components/auth/AuthProvider';
export default function AppRootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="es">
<body className="app-theme">
{/* Los proveedores pesados de sesión solo se montan aquí */}
<AuthProvider>
{children}
</AuthProvider>
</body>
</html>
);
}¿Qué sucede en el navegador cuando un usuario hace clic en un enlace de /pricing (app/(marketing)/layout.tsx) hacia /dashboard (app/(app)/layout.tsx)?
En productos comerciales es prioritario que las páginas de aterrizaje carguen con métricas óptimas de Core Web Vitals (LCP < 1.2s). Con Route Groups se pueden confinar los proveedores de autenticación y WebSockets al grupo (app), manteniendo (marketing) totalmente ligero:
app/
├── layout.tsx (Shell raíz común)
├── (marketing)/
│ ├── layout.tsx ──► Bundle JS de cliente cero: SSR/SSG puro
│ └── page.tsx ──► LCP: 0.6s (CDN Edge Cache)
└── (workspace)/
├── layout.tsx ──► Contiene AuthContext, WebSocketProvider, ReactQuery
└── editor/page.tsx ──► Entorno de trabajo interactivoDado que las carpetas entre paréntesis se omiten de la URL, crear rutas relativas idénticas en grupos distintos causa un error fatal en Turbopack:
app/(marketing)/about/page.tsx $\to$ /aboutapp/(shop)/about/page.tsx $\to$ /about
Solución: Asegura la unicidad de las rutas finales y aplica reglas de linting de directorios (eslint-plugin-check-file).Si la aplicación se divide en varios Root Layouts, transicionar entre ellos fuerza una recarga completa (window.location), perdiendo datos no guardados en memoria.
Solución: Emplea un único app/layout.tsx para el 95% de los proyectos, reservando los Route Groups para layouts anidados.