Next.js Route Groups — Organize Without URL

Sanjeev SharmaSanjeev Sharma
4 min read

Advertisement

Next.js Route Groups — Organize Without URL

Route groups enable logical organization of your codebase without changing public URLs, making large applications more maintainable.

What Are Route Groups?

Directories wrapped in parentheses are route groups. They organize files without appearing in the URL:

app/
├── (marketing)/
│   ├── page.tsx/
│   └── about/
│       └── page.tsx/about
├── (admin)/
│   ├── page.tsx/
│   └── users/
│       └── page.tsx/users

The parentheses don't affect URLs, so both groups can have their own page.tsx at /.

Basic Organization

Group related pages together:

app/
├── (auth)/
│   ├── login/
│   │   └── page.tsx/login
│   ├── register/
│   │   └── page.tsx/register
│   └── forgot-password/
│       └── page.tsx/forgot-password
├── (dashboard)/
│   ├── page.tsx/
│   ├── analytics/
│   │   └── page.tsx/analytics
│   └── settings/
│       └── page.tsx/settings

Multiple Route Groups

Create different layouts for different groups:

// app/(marketing)/layout.tsx
export default function MarketingLayout({ children }) {
  return (
    <div>
      <nav>Marketing Navigation</nav>
      {children}
    </div>
  )
}
// app/(admin)/layout.tsx
export default function AdminLayout({ children }) {
  return (
    <div className="flex">
      <aside>Admin Sidebar</aside>
      <main>{children}</main>
    </div>
  )
}

Real-World Structure

Organize a SaaS app:

app/
├── layout.tsxRoot layout
├── (marketing)/
│   ├── layout.tsxMarketing layout
│   ├── page.tsx/ (landing)
│   ├── pricing/
│   │   └── page.tsx/pricing
│   ├── features/
│   │   └── page.tsx/features
│   └── blog/
│       ├── page.tsx/blog
│       └── [slug]/
│           └── page.tsx/blog/[slug]
├── (app)/
│   ├── layout.tsxApp layout (with sidebar)
│   ├── page.tsx/app (dashboard)
│   ├── projects/
│   │   ├── page.tsx/projects
│   │   └── [id]/
│   │       └── page.tsx/projects/[id]
│   └── settings/
│       ├── page.tsx/settings
│       └── [section]/
│           └── page.tsx/settings/[section]
├── (auth)/
│   ├── layout.tsxAuth layout
│   ├── login/
│   │   └── page.tsx/login
│   ├── register/
│   │   └── page.tsx/register
│   └── reset-password/
│       └── page.tsx/reset-password
└── api/
    └── route.ts/api

Shared Components Between Groups

Share components across groups:

app/
├── components/Shared components
│   ├── navbar.tsx
│   ├── button.tsx
│   └── card.tsx
├── (marketing)/
│   ├── layout.tsx
│   └── page.tsx
└── (admin)/
    ├── layout.tsx
    └── page.tsx
// app/(marketing)/layout.tsx
import { Navbar } from '@/components/navbar'

export default function MarketingLayout({ children }) {
  return (
    <div>
      <Navbar />
      {children}
    </div>
  )
}
// app/(admin)/layout.tsx
import { Navbar } from '@/components/navbar'

export default function AdminLayout({ children }) {
  return (
    <div>
      <Navbar />
      <div className="flex">
        <Sidebar />
        <main>{children}</main>
      </div>
    </div>
  )
}

Nested Route Groups

Create hierarchies of groups:

app/
├── (site)/
├── (auth)/
│   │   ├── login/
│   │   │   └── page.tsx/login
│   │   └── register/
│   │       └── page.tsx/register
├── (public)/
│   │   ├── page.tsx/
│   │   └── about/
│   │       └── page.tsx/about
│   └── layout.tsxSite layout
└── (admin)/
    ├── layout.tsxAdmin layout
    └── dashboard/
        └── page.tsx/dashboard

Organize by feature:

app/
├── (user)/
│   ├── profile/
│   │   ├── page.tsx
│   │   └── edit/
│   │       └── page.tsx
│   └── notifications/
│       └── page.tsx
├── (blog)/
│   ├── page.tsx
│   ├── [slug]/
│   │   └── page.tsx
│   └── category/
│       └── [name]/
│           └── page.tsx
├── (api)/
│   └── posts/
│       └── route.ts

Middleware with Route Groups

Apply middleware to specific groups:

// middleware.ts
import { NextRequest, NextResponse } from 'next/server'

export function middleware(request: NextRequest) {
  // Protect admin group
  if (request.nextUrl.pathname.startsWith('/admin')) {
    const hasAuth = request.cookies.get('auth-token')
    if (!hasAuth) {
      return NextResponse.redirect(new URL('/login', request.url))
    }
  }

  return NextResponse.next()
}

export const config = {
  matcher: ['/((?!api|_next/static).*)']
}

FAQ

Q: Can route groups contain other route groups? A: Yes, create nested hierarchies. (group1)/(group2)/page.tsx is valid.

Q: Do route groups affect SEO? A: No, route groups only affect file organization. URLs and metadata are unaffected.

Q: Can I have a page.tsx both inside and outside a route group? A: Yes. page.tsx in (group1) and page.tsx in (group2) can both exist at the same URL level, but layout.tsx files control rendering.


Use route groups to maintain clean, scalable project structure.

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro