Route Groups
Route groups use (folderName) parentheses to organize routes without affecting the URL structure. They enable multiple layouts at the same route level.
Recipe
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.
Working Example
// 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>;
}Deep Dive
How It Works
- Parenthesized folders are URL-invisible.
(marketing)never appears in the URL — it is purely an organizational mechanism. - Each group can have its own layout. This is the primary use case: different visual shells for different sections of the site.
- Groups share the root layout. The root
app/layout.tsxstill wraps everything. Group layouts nest inside it. - You can have multiple root layouts by removing the top-level
app/layout.tsxand placing alayout.tsxwith<html>and<body>inside each group. Each group then has a completely independent document. - Route groups can be nested.
(marketing)/(campaigns)/page.tsxis valid — both group segments are stripped. - Groups do not create route boundaries. A
loading.tsxorerror.tsxinside a group works the same as in any folder. - No conflicts between groups at the same path. You cannot have
(marketing)/about/page.tsxAND(app)/about/page.tsx— both resolve to/aboutand Next.js will error.
Variations
# 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
TypeScript Notes
// 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 }>;
}Gotchas
- Two groups cannot define the same route. If
(a)/about/page.tsxand(b)/about/page.tsxboth exist, the build fails. - The home page
/can only live in one group. Placepage.tsxin the group that should own the root URL. - Multiple root layouts means no shared layout. If each group has its own
<html>, navigating between groups triggers a full page reload. - Nested groups compound.
(a)/(b)/page.tsxis allowed, but both layers are stripped — the URL is just/. loading.tsxanderror.tsxin a group apply to all routes inside that group — they do not leak into other groups.- Middleware does not know about groups. Middleware matchers work on actual URL paths (without the parenthesized names).
Alternatives
| 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 |
FAQs
Does the parenthesized folder name appear in the URL?
No. The parenthesized name is stripped entirely. (marketing)/about/page.tsx serves /about, not /(marketing)/about.
Can two route groups define a page at the same URL path?
No. If (a)/about/page.tsx and (b)/about/page.tsx both exist, the build fails because both resolve to /about.
Which route group should own the home page (/)?
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).
What happens when you navigate between routes in different groups that have separate root layouts?
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.
Do loading.tsx and error.tsx in a route group leak into other groups?
No. They apply only to routes within that group. Each group's boundary files are scoped to its own routes.
Gotcha: Does middleware know about route group names?
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.
How do you create multiple root layouts?
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
Can route groups be nested?
Yes. (a)/(b)/page.tsx is valid. Both group segments are stripped, so the URL is just /.
What TypeScript types do route group layouts use?
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;
}How do you type a page inside a route group that also has a dynamic segment above it?
// app/(app)/[orgId]/settings/page.tsx
interface SettingsPageProps {
params: Promise<{ orgId: string }>;
}Params from dynamic segments above the group still flow through normally.
Gotcha: When using groups for auth separation, how do you protect the app group?
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>;
}Are route groups useful even when you do not need different layouts?
Yes. They can be used purely for file-system organization, grouping related routes together without affecting the URL structure.
Related
- App Router Basics — file conventions overview
- Layouts — how layouts compose and nest
- Parallel Routes — @slot segments for side-by-side rendering
- Middleware — request-level routing logic