Route Groups
Route groups use (folderName) parentheses to organize routes without affecting the URL structure. They enable multiple layouts at the same route level.
Search across all documentation pages
Route groups use (folderName) parentheses to organize routes without affecting the URL structure. They enable multiple layouts at the same route level.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Quick-reference recipe card - copy-paste ready.
app/
├── (marketing)/
│ ├── layout.tsx # Marketing layout (no nav, full-width)
│ ├── page.tsx # / (home page)
│ ├── about/page.tsx # /about
│ └── pricing/page.tsx # /pricing
├── (app)/
│ ├── layout.tsx # App layout (sidebar, auth required)
│ ├── dashboard/page.tsx # /dashboard
│ └── settings/page.tsx # /settings
└── layout.tsx # Root layout (shared by both groups)
Key rule: The parenthesized folder name is stripped from the URL. (marketing)/about/page.tsx serves /about, not /(marketing)/about.
// app/layout.tsx - Root layout shared by all groups
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}// app/(marketing)/layout.tsx - Public marketing pages
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 - Home page at /
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 - Authenticated app pages
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 at /dashboard
export default function DashboardPage() {
return <h1>Dashboard</h1>;
}(marketing) never appears in the URL - it is purely an organizational mechanism.app/layout.tsx still wraps everything. Group layouts nest inside it.app/layout.tsx and placing a layout.tsx with <html> and <body> inside each group. Each group then has a completely independent document.(marketing)/(campaigns)/page.tsx is valid - both group segments are stripped.loading.tsx or error.tsx inside a group works the same as in any folder.(marketing)/about/page.tsx AND (app)/about/page.tsx - both resolve to /about and Next.js will error.# Multiple root layouts (completely separate HTML documents)
app/
├── (marketing)/
│ ├── layout.tsx # Must include <html> and <body>
│ └── page.tsx # /
├── (app)/
│ ├── layout.tsx # Must include <html> and <body>
│ └── dashboard/
│ └── page.tsx # /dashboard
# No top-level app/layout.tsx in this pattern
// Using groups for auth vs. public separation
// 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# Groups for feature organization (no layout difference)
app/
├── (features)/
│ ├── billing/page.tsx # /billing
│ └── invoices/page.tsx # /invoices
├── (admin)/
│ ├── layout.tsx # Admin layout with guard
│ └── users/page.tsx # /users
// Route group layouts have the same types as any layout
interface GroupLayoutProps {
children: React.ReactNode;
}
// No special types needed - groups are purely a file-system concept
// Params from dynamic segments above the group still flow through
// app/(app)/[orgId]/settings/page.tsx
interface SettingsPageProps {
params: Promise<{ orgId: string }>;
}(a)/about/page.tsx and (b)/about/page.tsx both exist, the build fails./ can only live in one group. Place page.tsx in the group that should own the root URL.<html>, navigating between groups triggers a full page reload.(a)/(b)/page.tsx is allowed, but both layers are stripped - the URL is just /.loading.tsx and error.tsx in a group apply to all routes inside that group - they do not leak into other groups.| Approach | When to Use |
|---|---|
| Nested folders without parens | When the folder should appear in the URL |
Parallel Routes (@slot) | Rendering multiple views simultaneously in one layout |
| Middleware-based redirects | Routing users to different sections based on auth or role |
| Separate Next.js apps | Completely independent deployments for different sections |
No. The parenthesized name is stripped entirely. (marketing)/about/page.tsx serves /about, not /(marketing)/about.
No. If (a)/about/page.tsx and (b)/about/page.tsx both exist, the build fails because both resolve to /about.
Only one group can contain page.tsx at its root. Place it in whichever group should own the / URL (typically the marketing or public group).
A full page reload occurs. When each group has its own <html> and <body> (multiple root layouts), navigating between groups cannot be a soft client-side transition.
No. They apply only to routes within that group. Each group's boundary files are scoped to its own routes.
No. Middleware matchers work on actual URL paths. The parenthesized folder names are invisible to middleware. Match on the real URL like /about, not /(marketing)/about.
Remove the top-level app/layout.tsx and place a layout.tsx with <html> and <body> inside each group.
app/
├── (marketing)/
│ ├── layout.tsx # Must include <html> and <body>
│ └── page.tsx
├── (app)/
│ ├── layout.tsx # Must include <html> and <body>
│ └── dashboard/page.tsx
Yes. (a)/(b)/page.tsx is valid. Both group segments are stripped, so the URL is just /.
The same types as any layout. There are no special types for route groups since they are purely a file-system concept.
interface GroupLayoutProps {
children: React.ReactNode;
}// app/(app)/[orgId]/settings/page.tsx
interface SettingsPageProps {
params: Promise<{ orgId: string }>;
}Params from dynamic segments above the group still flow through normally.
Check auth in the group's layout and redirect if unauthenticated.
// 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>;
}Yes. They can be used purely for file-system organization, grouping related routes together without affecting the URL structure.
Reviewed by Chris St. John·Last updated Jul 10, 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥