How and Why to use Route Groups in NextJS?
Route groups in Next.js 14 App Router let you organize routes without affecting the URL structure. This is particularly useful when managing large applications with different sections. In this tutorial, weβll explore how route groups work, how to set up layouts, and how nested components settle with their parent layouts.
Setting Up a Route Group
Letβs start by organizing our Recipe App into public-facing pages (such as recipe listings) and admin pages (for managing recipes). Weβll use route groups to keep the structure clean and manageable.
Hereβs a tree structure for the app:
src/
βββ app/
βββ (public)/
β βββ layout.tsx
β βββ page.tsx
β βββ recipes/
β βββ page.tsx
βββ (admin)/
βββ layout.tsx
βββ dashboard/
β βββ page.tsx
βββ recipes/
βββ add/
βββ page.tsx
Public Layout and Page Example
The (public) folder represents public-facing pages like the home page and recipe listings. This folder wonβt affect the URL structure, meaning /recipes
will still work as expected, even though the page is inside the (public)
folder.
// src/app/(public)/layout.tsx
export default function PublicLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<header>
<h1>Recipe App</h1>
<nav>
<a href="/">Home</a> | <a href="/recipes">Recipes</a>
</nav>
</header>
<main>{children}</main>
<footer>Β© 2024 Recipe App</footer>
</body>
</html>
);
}
// src/app/(public)/page.tsx
export default function HomePage() {
return (
<section>
<h2>Welcome to the Recipe App</h2>
<p>Find and share the best recipes!</p>
</section>
);
}
In this example, layout.tsx
wraps all public pages, such as /recipes
, with a shared header and footer.
Final HTML output for /
:
<html lang="en">
<body>
<header>
<h1>Recipe App</h1>
<nav>
<a href="/">Home</a> | <a href="/recipes">Recipes</a>
</nav>
</header>
<main>
<section>
<h2>All Recipes</h2>
<p>Find and share the best recipes!</p>
</section>
</main>
<footer>Β© 2024 Recipe App</footer>
</body>
</html>
Nested Route Groups
How nested RouteGroup works in Next.js?
You can nest route groups to organize different sections of your app. For example, you can group admin pages under an (admin)
folder, which will include pages like the dashboard and add recipe.
// src/app/(admin)/layout.tsx
export default function AdminLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<nav>
<a href="/dashboard">Dashboard</a> | <a href="/admin/recipes/add">Add Recipe</a>
</nav>
<main>{children}</main>
</body>
</html>
);
}
// src/app/(admin)/dashboard/page.tsx
export default function DashboardPage() {
return (
<section>
<h2>Admin Dashboard</h2>
<p>Manage your recipes and content.</p>
</section>
);
}
In this case, /dashboard
uses the admin layout, which provides navigation links specific to admin actions, like adding recipes.
Final HTML output for /dashboard
:
<html lang="en">
<body>
<nav>
<a href="/dashboard">Dashboard</a> | <a href="/admin/recipes/add">Add Recipe</a>
</nav>
<main>
<section>
<h2>Admin Dashboard</h2>
<p>Manage your recipes and content.</p>
</section>
</main>
</body>
</html>
Nested Routes for Admin Actions (Adding Recipes)
Nested route groups help you organize admin actions, such as adding new recipes. Letβs look at the add recipe
page under the admin section:
// src/app/(admin)/recipes/add/page.tsx
export default function AddRecipePage() {
return (
<section>
<h2>Add New Recipe</h2>
<form>
<label htmlFor="recipe-name">Recipe Name:</label>
<input type="text" id="recipe-name" />
<button type="submit">Add Recipe</button>
</form>
</section>
);
}
This page maps to /admin/recipes/add
and is wrapped with the admin layout. The AddRecipePage
component will be rendered inside the <main>
tag of AdminLayout
.
Final HTML output for /admin/recipes/add
:
<html lang="en">
<body>
<nav>
<a href="/dashboard">Dashboard</a> | <a href="/admin/recipes/add">Add Recipe</a>
</nav>
<main>
<section>
<h2>Add New Recipe</h2>
<form>
<label for="recipe-name">Recipe Name:</label>
<input type="text" id="recipe-name" />
<button type="submit">Add Recipe</button>
</form>
</section>
</main>
</body>
</html>
Excluding Layouts from Specific Routes
Sometimes, you want specific pages not to inherit a layout. To exclude a layout, you can create an empty layout file for that route group. Hereβs how you can exclude the layout for a specific page:
// src/app/(public)/(no-layout)/layout.tsx
export default function NoLayout({ children }: { children: React.ReactNode }) {
return <>{children}</>; // No layout, just render the content
}
// src/app/(public)/(no-layout)/page.tsx
export default function NoLayoutPage() {
return (
<section>
<h2>No Layout Page</h2>
<p>This page does not use any layout.</p>
</section>
);
}
In this setup, the NoLayoutPage
will not inherit the public layout, keeping the page isolated from any shared components.
Final HTML output for /no-layout
:
<html lang="en">
<body>
<section>
<h2>No Layout Page</h2>
<p>This page does not use any layout.</p>
</section>
</body>
</html>
Summary
Route groups in Next.js 14 App Router let you organize your app without impacting the URL structure. By grouping public and admin pages under different folders, you can manage layouts more effectively and keep your app structure clean. Nested route groups make it easy to manage different sections of your app, and you can even exclude layouts for specific pages to tailor their structure. Each nested component smoothly integrates with the parent layout, making your app both maintainable and scalable.