Grupos de Rutas
Los grupos de rutas usan paréntesis (folderName) para organizar rutas sin afectar la estructura de URLs. Permiten múltiples layouts en el mismo nivel de ruta.
Busca en todas las páginas de la documentación
Los grupos de rutas usan paréntesis (folderName) para organizar rutas sin afectar la estructura de URLs. Permiten múltiples layouts en el mismo nivel de ruta.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Tarjeta de receta de referencia rápida - lista para copiar-pegar.
app/
├── (marketing)/
│ ├── layout.tsx # Layout de marketing (sin nav, ancho completo)
│ ├── page.tsx # / (página de inicio)
│ ├── about/page.tsx # /about
│ └── pricing/page.tsx # /pricing
├── (app)/
│ ├── layout.tsx # Layout de aplicación (barra lateral, autenticación requerida)
│ ├── dashboard/page.tsx # /dashboard
│ └── settings/page.tsx # /settings
└── layout.tsx # Layout raíz (compartido por ambos grupos)
Regla clave: El nombre de carpeta entre paréntesis se elimina de la URL. (marketing)/about/page.tsx sirve /about, no /(marketing)/about.
// app/layout.tsx - Layout raíz compartido por todos los grupos
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}// app/(marketing)/layout.tsx - Páginas públicas de marketing
import Link from "next/link";
export default function MarketingLayout({ children }: { children: React.ReactNode }) {
return (
<div>
<header className="flex items-center justify-between px-8 py-4">
<Link href="/" className="text-xl font-bold">Brand</Link>
<nav className="space-x-4">
<Link href="/about">About</Link>
<Link href="/pricing">Pricing</Link>
<Link href="/dashboard">Sign In</Link>
</nav>
</header>
<main>{children}</main>
</div>
);
}// app/(marketing)/page.tsx - Página de inicio en /
export default function HomePage() {
return (
<section className="py-20 text-center">
<h1 className="text-5xl font-bold">Welcome to Our Product</h1>
<p className="mt-4 text-lg text-gray-600">The best solution for your needs.</p>
</section>
);
}// app/(app)/layout.tsx - Páginas de aplicación autenticadas
import { auth } from "@/lib/auth";
import { redirect } from "next/navigation";
import Link from "next/link";
export default async function AppLayout({ children }: { children: React.ReactNode }) {
const session = await auth();
if (!session) redirect("/login");
return (
<div className="flex min-h-screen">
<aside className="w-64 border-r p-4">
<nav className="space-y-2">
<Link href="/dashboard" className="block">Dashboard</Link>
<Link href="/settings" className="block">Settings</Link>
</nav>
</aside>
<main className="flex-1 p-8">{children}</main>
</div>
);
}// app/(app)/dashboard/page.tsx - Dashboard en /dashboard
export default function DashboardPage() {
return <h1>Dashboard</h1>;
}(marketing) nunca aparece en la URL - es puramente un mecanismo de organización.app/layout.tsx raíz aún envuelve todo. Los layouts de grupo se anidan dentro de él.app/layout.tsx y colocando un layout.tsx con <html> y <body> dentro de cada grupo. Cada grupo entonces tiene un documento completamente independiente.(marketing)/(campaigns)/page.tsx es válido - ambos segmentos de grupo se eliminan.loading.tsx o error.tsx dentro de un grupo funciona igual que en cualquier carpeta.(marketing)/about/page.tsx Y (app)/about/page.tsx - ambas se resuelven a /about y Next.js lanzará un error.# Múltiples layouts raíz (documentos HTML completamente separados)
app/
├── (marketing)/
│ ├── layout.tsx # Debe incluir <html> y <body>
│ └── page.tsx # /
├── (app)/
│ ├── layout.tsx # Debe incluir <html> y <body>
│ └── dashboard/
│ └── page.tsx # /dashboard
# Sin app/layout.tsx de nivel superior en este patrón
// Usando grupos para separación de autenticación vs. público
// app/(auth)/layout.tsx
export default function AuthLayout({ children }: { children: React.ReactNode }) {
return (
<div className="flex min-h-screen items-center justify-center">
<div className="w-full max-w-md">{children}</div>
</div>
);
}
// app/(auth)/login/page.tsx → /login
// app/(auth)/register/page.tsx → /register# Grupos para organización de características (sin diferencia de layout)
app/
├── (features)/
│ ├── billing/page.tsx # /billing
│ └── invoices/page.tsx # /invoices
├── (admin)/
│ ├── layout.tsx # Layout de administrador con protección
│ └── users/page.tsx # /users
// Los layouts de grupo de ruta tienen los mismos tipos que cualquier layout
interface GroupLayoutProps {
children: React.ReactNode;
}
// No se necesitan tipos especiales - los grupos son puramente un concepto del sistema de archivos
// Los params de segmentos dinámicos por encima del grupo aún fluyen a través
// app/(app)/[orgId]/settings/page.tsx
interface SettingsPageProps {
params: Promise<{ orgId: string }>;
}(a)/about/page.tsx y (b)/about/page.tsx ambas existen, la compilación falla./ solo puede vivir en un grupo. Coloca page.tsx en el grupo que debe poseer la URL raíz.<html>, navegar entre grupos activa una recarga completa de la página.(a)/(b)/page.tsx está permitido, pero ambas capas se eliminan - la URL es solo /.loading.tsx y error.tsx en un grupo se aplican a todas las rutas dentro de ese grupo - no se filtran a otros grupos.| Enfoque | Cuándo Usar |
|---|---|
| Carpetas anidadas sin paréntesis | Cuando la carpeta debe aparecer en la URL |
Rutas Paralelas (@slot) | Renderizar múltiples vistas simultáneamente en un layout |
| Redirecciones basadas en middleware | Enrutar usuarios a diferentes secciones según autenticación o rol |
| Aplicaciones Next.js separadas | Despliegues completamente independientes para diferentes secciones |
No. El nombre entre paréntesis se elimina completamente. (marketing)/about/page.tsx sirve /about, no /(marketing)/about.
No. Si (a)/about/page.tsx y (b)/about/page.tsx ambas existen, la compilación falla porque ambas se resuelven a /about.
Solo un grupo puede contener page.tsx en su raíz. Colócalo en el grupo que debe poseer la URL / (típicamente el grupo de marketing o público).
Ocurre una recarga completa de la página. Cuando cada grupo tiene su propio <html> y <body> (múltiples layouts raíz), navegar entre grupos no puede ser una transición suave del lado del cliente.
No. Se aplican solo a rutas dentro de ese grupo. Los archivos límite de cada grupo están limitados a sus propias rutas.
No. Los matchers de middleware funcionan en rutas de URL reales. Los nombres de carpetas entre paréntesis son invisibles para middleware. Coincide en la URL real como /about, no /(marketing)/about.
Elimina el nivel superior app/layout.tsx y coloca un layout.tsx con <html> y <body> dentro de cada grupo.
app/
├── (marketing)/
│ ├── layout.tsx # Debe incluir <html> y <body>
│ └── page.tsx
├── (app)/
│ ├── layout.tsx # Debe incluir <html> y <body>
│ └── dashboard/page.tsx
Sí. (a)/(b)/page.tsx es válido. Ambos segmentos de grupo se eliminan, así que la URL es solo /.
Los mismos tipos que cualquier layout. No hay tipos especiales para grupos de rutas ya que son puramente un concepto del sistema de archivos.
interface GroupLayoutProps {
children: React.ReactNode;
}// app/(app)/[orgId]/settings/page.tsx
interface SettingsPageProps {
params: Promise<{ orgId: string }>;
}Los params de segmentos dinámicos por encima del grupo aún fluyen normalmente.
Verifica la autenticación en el layout del grupo y redirige si no está autenticado.
// app/(app)/layout.tsx
import { auth } from "@/lib/auth";
import { redirect } from "next/navigation";
export default async function AppLayout({
children,
}: { children: React.ReactNode }) {
const session = await auth();
if (!session) redirect("/login");
return <div>{children}</div>;
}Sí. Pueden usarse puramente para organización del sistema de archivos, agrupando rutas relacionadas sin afectar la estructura de URLs.
Revisado por Chris St. John·Última actualización: 10 jul 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥