At the heart of the Quantum Metropolis, the urban space management system is based on the concept of thematic districts and functional zones. The city is divided into recognizable areas - a science district, residential zone, entertainment district, and many others. These zones are separated from each other but work together as elements of a single, cohesive urban organism.
Similarly, in Next.js 15, we can organize and group routes in our application to increase code clarity, improve management, and enable more flexible loading strategies and component isolation.
In App Router, we can create route groups that have no impact on the actual URL structure by placing a directory in parentheses, for example:
(groupName). This is a powerful feature that allows for logical code organization without changing its behavior or public API.Route groups are defined by placing the directory name in parentheses:
1app/
2 βββ (marketing)/ # Group for marketing pages
3 β βββ about/
4 β β βββ page.tsx # /about
5 β βββ blog/
6 β β βββ page.tsx # /blog
7 β βββ layout.tsx # Shared layout for marketing pages
8 β
9 βββ (shop)/ # Group for marketing pages
10 β βββ products/
11 β β βββ page.tsx # /products
12 β βββ cart/
13 β β βββ page.tsx # /cart
14 β βββ layout.tsx # Shared layout for shop pages
15 β
16 βββ layout.tsx # Main application layoutIn this example, the
/about and /blog routes are part of the (marketing) group, while /products and /cart are part of the (shop) group. Note that route groups do not affect the actual URLs - the (marketing) directory is not part of the /about URL path.The simplest use of route groups is logical file organization, especially in larger applications:
1app/
2 βββ (main)/ # Main public pages
3 β βββ page.tsx # Home (/)
4 β βββ about/ # /about
5 β βββ contact/ # /contact
6 β
7 βββ (auth)/ # Authentication-related pages
8 β βββ login/ # /login
9 β βββ register/ # /register
10 β βββ forgot-password/ # /forgot-password
11 β
12 βββ (dashboard)/ # Dashboard pages (protected)
13 β βββ account/ # /account
14 β βββ settings/ # /settings
15 β βββ billing/ # /billing
16 β
17 βββ layout.tsx # Main layoutOne of the most powerful uses of route groups is the ability to define shared layouts for specific groups of pages without affecting the URL paths:
1// app/(marketing)/layout.tsx - Layout for marketing pages
2export default function MarketingLayout({
3 children,
4}: {
5 children: React.ReactNode;
6}) {
7 return (
8 <div className="marketing-layout">
9 <nav className="marketing-nav">
10 <a href="/about">About Us</a>
11 <a href="/blog">Blog</a>
12 <a href="/careers">Careers</a>
13 </nav>
14 <main>{children}</main>
15 <footer className="marketing-footer">
16 <p>Β© 2150 Quantum Voyages. All rights reserved.</p>
17 </footer>
18 </div>
19 );
20}1// app/(shop)/layout.tsx - Layout for shop pages
2export default function ShopLayout({
3 children,
4}: {
5 children: React.ReactNode;
6}) {
7 return (
8 <div className="shop-layout">
9 <header className="shop-header">
10 <h1>Quantum Voyages Store</h1>
11 <nav className="shop-nav">
12 <a href="/products">Products</a>
13 <a href="/categories">Categories</a>
14 <a href="/cart">Cart</a>
15 </nav>
16 </header>
17 <main>{children}</main>
18 <aside className="shop-sidebar">
19 <div className="popular-products">
20 <h3>Popular Products</h3>
21 {/* List of popular products */}
22 </div>
23 </aside>
24 </div>
25 );
26}Route groups are particularly useful in multi-section applications, such as an application with a public homepage, admin panel, and user dashboard:
1app/
2 βββ (public)/ # Public part of the website
3 β βββ page.tsx # Home (/)
4 β βββ about/ # /about
5 β βββ blog/ # /blog
6 β β βββ [slug]/ # /blog/:slug
7 β βββ layout.tsx # Public layout
8 β
9 βββ (admin)/ # Admin panel
10 β βββ admin/ # /admin (uwaga: potrzebny katalog admin/ dla zachowania URL)
11 β β βββ page.tsx # /admin
12 β β βββ users/ # /admin/users
13 β β βββ settings/ # /admin/settings
14 β βββ layout.tsx # Admin layout
15 β
16 βββ (dashboard)/ # User dashboard
17 β βββ dashboard/ # /dashboard
18 β β βββ page.tsx # /dashboard
19 β β βββ profile/ # /dashboard/profile
20 β β βββ settings/ # /dashboard/settings
21 β βββ layout.tsx # Dashboard layout
22 β
23 βββ layout.tsx # Main layout (shared across all sections)In this example, the application is divided into three main sections, each with its own layout, but all sharing a common main layout.
Let's implement a route group system for our space application Quantum Voyages:
1app/
2 βββ (marketing)/ # Marketing pages
3 β βββ page.tsx # Home (/)
4 β βββ about/ # /about
5 β βββ destinations/ # /destinations
6 β β βββ [id]/ # /destinations/:id
7 β βββ layout.tsx # Marketing layout
8 β
9 βββ (customer)/ # Pages for logged-in customers
10 β βββ dashboard/ # /dashboard
11 β βββ bookings/ # /bookings
12 β β βββ [id]/ # /bookings/:id
13 β βββ account/ # /account
14 β βββ layout.tsx # Customer layout
15 β
16 βββ (admin)/ # Admin panel
17 β βββ admin/ # /admin
18 β β βββ voyages/ # /admin/voyages
19 β β βββ customers/ # /admin/customers
20 β β βββ analytics/ # /admin/analytics
21 β βββ layout.tsx # Admin layoutistracyjny
22 β
23 βββ layout.tsx # Main layoutCode for the layouts:
1// app/layout.tsx - Main layout
2import { Inter } from 'next/font/google';
3import './globals.css';
4
5const inter = Inter({ subsets: ['latin'] });
6
7export const metadata = {
8 title: 'Quantum Voyages - Space Travels from the Quantum Metropolis',
9 description: 'Discover space with the Quantum Metropolis guide',
10};
11
12export default function RootLayout({
13 children,
14}: {
15 children: React.ReactNode;
16}) {
17 return (
18 <html lang="pl">
19 <body className={inter.className}>
20 {children}
21 </body>
22 </html>
23 );
24}1// app/(marketing)/layout.tsx - Marketing layout
2import Header from '@/components/marketing/Header';
3import Footer from '@/components/marketing/Footer';
4
5export default function MarketingLayout({
6 children,
7}: {
8 children: React.ReactNode;
9}) {
10 return (
11 <>
12 <Header />
13 <main className="min-h-screen pt-16 pb-20">
14 {children}
15 </main>
16 <Footer />
17 </>
18 );
19}1// app/(customer)/layout.tsx - Layout for logged-in customers
2import CustomerHeader from '@/components/customer/Header';
3import CustomerSidebar from '@/components/customer/Sidebar';
4import { checkAuth } from '@/lib/auth';
5import { redirect } from 'next/navigation';
6
7export default async function CustomerLayout({
8 children,
9}: {
10 children: React.ReactNode;
11}) {
12 // Check if the user is logged in
13 const user = await checkAuth();
14 if (!user) {
15 redirect('/login?returnTo=' + encodeURIComponent(window.location.pathname));
16 }
17
18 return (
19 <>
20 <CustomerHeader user={user} />
21 <div className="flex min-h-screen pt-16">
22 <CustomerSidebar />
23 <main className="flex-1 p-6 bg-gray-50">
24 {children}
25 </main>
26 </div>
27 </>
28 );
29}1// app/(admin)/layout.tsx - Admin layout
2import AdminHeader from '@/components/admin/Header';
3import AdminSidebar from '@/components/admin/Sidebar';
4import { checkAdminAuth } from '@/lib/auth';
5import { redirect } from 'next/navigation';
6
7export default async function AdminLayout({
8 children,
9}: {
10 children: React.ReactNode;
11}) {
12 // Check if the user is an admin
13 const isAdmin = await checkAdminAuth();
14 if (!isAdmin) {
15 redirect('/admin-login');
16 }
17
18 return (
19 <div className="admin-layout">
20 <AdminHeader />
21 <div className="flex min-h-screen pt-16">
22 <AdminSidebar />
23 <main className="flex-1 p-6 bg-gray-100">
24 {children}
25 </main>
26 </div>
27 </div>
28 );
29}In Next.js 15, we can also create option groups that serve solely for code organization, without any impact on routing or loading logic:
1app/
2 βββ (onboarding)/welcome/
3 β βββ page.tsx # /welcome
4 βββ (payments)/cart/
5 βββ page.tsx # /cartRoute groups are particularly useful when we want to share a layout between specific routes that aren't necessarily close to each other in the URL hierarchy:
1app/
2 βββ (auth)/
3 β βββ login/ # /login
4 β β βββ page.tsx
5 β βββ register/ # /register
6 β β βββ page.tsx
7 β βββ layout.tsx # Shared layout only for /login and /register
8 β
9 βββ account/ # /account (does not use the (auth) layout)
10 β βββ page.tsx
11 β
12 βββ layout.tsx # Main layout dla wszystkich trasNext.js 15 also introduces the private folders convention, which are marked with an underscore (_) at the beginning of the name. Such folders are not included in URL paths but can contain code shared by multiple routes.
1app/
2 βββ _components/ # Private folder for shared components
3 β βββ Button.tsx # Button component
4 β βββ Card.tsx # Card component
5 β βββ SearchForm.tsx # Search form component
6 β
7 βββ _lib/ # Private folder for shared functions
8 β βββ api.ts # API functions
9 β βββ utils.ts # Utility functions
10 β
11 βββ destinations/ # Public route
12 β βββ page.tsx # /destinations
13 β
14 βββ page.tsx # Home (/)In this example, the
_components and _lib directories are private and do not generate any URL paths. They are used to store shared components and functions that can be imported by other components.Private folders and route groups look similar but have different use cases:
_folderName) are used to organize code that should not be directly accessible via URL.(folderName)) are used to group routes without affecting URL paths, but they can influence layout behavior.1app/
2 βββ _components/ # Shared UI components
3 β βββ ui/ # Basic UI components
4 β β βββ Button.tsx
5 β β βββ Card.tsx
6 β β βββ Input.tsx
7 β β
8 β βββ marketing/ # Components for marketing pages
9 β β βββ HeroSection.tsx
10 β β βββ TestimonialCard.tsx
11 β β
12 β βββ bookings/ # Components for the booking system
13 β βββ BookingForm.tsx
14 β βββ PlanetSelector.tsx
15 β
16 βββ _lib/ # Shared functions and utilities
17 β βββ api/ # Functions for API communication
18 β β βββ destinations.ts
19 β β βββ bookings.ts
20 β β
21 β βββ hooks/ # Custom React hooks
22 β β βββ useBooking.ts
23 β β βββ useAuth.ts
24 β β
25 β βββ utils/ # Helper functions
26 β βββ dates.ts
27 β βββ validation.ts
28 β
29 βββ _data/ # Static data (you can also use /public)
30 β βββ destinations.json
31 β βββ testimonials.json
32 β
33 βββ (marketing)/ # Group for marketing pages
34 β βββ page.tsx # Home (/)
35 β βββ about/ # /about
36 β βββ destinations/ # /destinations
37 β
38 βββ (customer)/ # Group for customer pages
39 βββ dashboard/ # /dashboard
40 βββ bookings/ # /bookingsExample code using private folders:
1// app/_components/ui/Button.tsx
2import { cva, type VariantProps } from 'class-variance-authority';
3import { cn } from '@/app/_lib/utils/cn';
4
5const buttonVariants = cva(
6 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
7 {
8 variants: {
9 variant: {
10 default: 'bg-primary text-primary-foreground hover:bg-primary/90',
11 destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
12 outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
13 secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
14 ghost: 'hover:bg-accent hover:text-accent-foreground',
15 link: 'text-primary underline-offset-4 hover:underline',
16 },
17 size: {
18 default: 'h-10 px-4 py-2',
19 sm: 'h-9 rounded-md px-3',
20 lg: 'h-11 rounded-md px-8',
21 icon: 'h-10 w-10',
22 },
23 },
24 defaultVariants: {
25 variant: 'default',
26 size: 'default',
27 },
28 }
29);
30
31export interface ButtonProps
32 extends React.ButtonHTMLAttributes<HTMLButtonElement>,
33 VariantProps<typeof buttonVariants> {
34 asChild?: boolean;
35}
36
37export function Button({
38 className,
39 variant,
40 size,
41 asChild = false,
42 ...props
43}: ButtonProps) {
44 return (
45 <button
46 className={cn(buttonVariants({ variant, size, className }))}
47 {...props}
48 />
49 );
50}1// app/(marketing)/destinations/page.tsx
2import { getDestinations } from '@/app/_lib/api/destinations';
3import DestinationCard from '@/app/_components/marketing/DestinationCard';
4import { HeroSection } from '@/app/_components/marketing/HeroSection';
5
6export default async function DestinationsPage() {
7 const destinations = await getDestinations();
8
9 return (
10 <div className="container mx-auto px-4 py-12">
11 <HeroSection
12 title="Discover Space with Quantum Voyages"
13 subtitle="Browse our space destinations and plan your adventure"
14 image="/images/space-banner.jpg"
15 />
16
17 <section className="my-12">
18 <h2 className="text-3xl font-bold mb-8">Popular Destinations</h2>
19
20 <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
21 {destinations.map((destination) => (
22 <DestinationCard
23 key={destination.id}
24 destination={destination}
25 />
26 ))}
27 </div>
28 </section>
29 </div>
30 );
31}Next.js 15 enables creating API endpoints using
route.js (or route.ts) files. These files can also benefit from organizational techniques such as route groups and private folders.1app/
2 βββ api/ # Main API section
3 β βββ (public)/ # Public endpoints
4 β β βββ destinations/
5 β β β βββ route.ts # GET /api/destinations
6 β β βββ newsletter/
7 β β βββ route.ts # POST /api/newsletter
8 β β
9 β βββ (protected)/ # Protected endpoints (require authorization)
10 β β βββ bookings/
11 β β β βββ route.ts # GET, POST /api/bookings
12 β β β βββ [id]/
13 β β β βββ route.ts # GET, PUT, DELETE /api/bookings/:id
14 β β βββ user/
15 β β βββ route.ts # GET /api/user
16 β β
17 β βββ _middleware/ # Middleware for API (private folder)
18 β βββ auth.ts # Authentication middleware
19 β βββ rateLimit.ts # Rate limiting middleware
20 β
21 βββ page.tsx # Home (/)Example implementation of a protected API endpoint:
1// app/api/(protected)/bookings/route.ts
2import { NextResponse } from 'next/server';
3import { verifyAuth } from '@/app/api/_middleware/auth';
4import { rateLimit } from '@/app/api/_middleware/rateLimit';
5
6export async function GET(request: Request) {
7 // Verify authentication
8 const authResult = await verifyAuth(request);
9 if (!authResult.isAuthenticated) {
10 return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
11 }
12
13 // Rate limiting
14 const rateLimitResult = await rateLimit(request);
15 if (!rateLimitResult.success) {
16 return NextResponse.json({ error: 'Too many requests' }, { status: 429 });
17 }
18
19 // Get bookings for the logged-in user
20 const userId = authResult.user.id;
21 const bookings = await getBookingsForUser(userId);
22
23 return NextResponse.json({ bookings });
24}
25
26export async function POST(request: Request) {
27 // Verify authentication
28 const authResult = await verifyAuth(request);
29 if (!authResult.isAuthenticated) {
30 return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
31 }
32
33 // Parse data from the request
34 const data = await request.json();
35
36 // Data validation
37 const validationResult = validateBookingData(data);
38 if (!validationResult.success) {
39 return NextResponse.json({ error: validationResult.errors }, { status: 400 });
40 }
41
42 // Create booking
43 const userId = authResult.user.id;
44 const booking = await createBooking(userId, data);
45
46 return NextResponse.json({ booking }, { status: 201 });
47}In the Quantum Metropolis, detailed planning is key to the efficient functioning of the city. Similarly, in larger Next.js 15 applications, it's important to follow specific rules and patterns for route organization:
Maintain a consistent directory structure and naming throughout the application:
1app/
2 βββ (sections)/ # Groups for main sections
3 β βββ (marketing)/ # Public section
4 β βββ (app)/ # Main application
5 β βββ (admin)/ # Admin panel
6 β
7 βββ _lib/ # Shared code
8 β βββ components/ # Shared components
9 β βββ hooks/ # React hooks
10 β βββ utils/ # Utility functions
11 β
12 βββ api/ # API endpoints
13 β βββ (public)/
14 β βββ (protected)/
15 β
16 βββ layout.tsx # Main layoutUse private folders to store shared components and place them at the appropriate hierarchy level:
app/_components/app/(section)/_components/page.js - Page rendering and data fetchinglayout.js - UI layout and navigation logicloading.js - Loading stateserror.js - Error handlingnot-found.js - Missing resource handlingConfigure import aliases to simplify paths:
1{
2 "compilerOptions": {
3 "baseUrl": ".",
4 "paths": {
5 "@/*": ["./*"],
6 "@components/*": ["./app/_components/*"],
7 "@lib/*": ["./app/_lib/*"],
8 "@hooks/*": ["./app/_lib/hooks/*"],
9 "@utils/*": ["./app/_lib/utils/*"]
10 }
11 }
12}This allows you to import components in a more readable way:
1// Instead of:
2import Button from '../../../_components/ui/Button';
3
4// You can use:
5import Button from '@components/ui/Button';Example of a comprehensive structure for a large Quantum Voyages application:
1app/
2 βββ (public)/ # Public part of the website
3 β βββ _components/ # Components specific to the public section
4 β βββ page.tsx # Home (/)
5 β βββ about/ # /about
6 β βββ destinations/ # /destinations
7 β β βββ page.tsx # Destination list
8 β β βββ [id]/ # /destinations/:id
9 β β βββ page.tsx # Destination details
10 β βββ contact/ # /contact
11 β βββ layout.tsx # Public layout
12 β
13 βββ (auth)/ # Authentication section
14 β βββ _components/ # Authentication components
15 β βββ _lib/ # Authentication logic
16 β βββ login/ # /login
17 β βββ register/ # /register
18 β βββ forgot-password/ # /forgot-password
19 β βββ layout.tsx # Authentication layout
20 β
21 βββ (app)/ # Main application (dla zalogowanych)
22 β βββ _components/ # Application components
23 β βββ dashboard/ # /dashboard
24 β βββ bookings/ # /bookings
25 β β βββ page.tsx # Booking list
26 β β βββ new/ # /bookings/new
27 β β βββ [id]/ # /bookings/:id
28 β β βββ page.tsx # Booking details
29 β β βββ edit/ # /bookings/:id/edit
30 β βββ account/ # /account
31 β β βββ page.tsx # Account overview
32 β β βββ profile/ # /account/profile
33 β β βββ preferences/ # /account/preferences
34 β βββ layout.tsx # Application layout
35 β
36 βββ (admin)/ # Admin panel
37 β βββ _components/ # Admin components
38 β βββ admin/ # /admin (potrzebny folder dla URL)
39 β β βββ page.tsx # Admin dashboard
40 β β βββ users/ # /admin/users
41 β β βββ destinations/ # /admin/destinations
42 β β βββ bookings/ # /admin/bookings
43 β βββ layout.tsx # Admin layouta
44 β
45 βββ _components/ # Shared components
46 β βββ ui/ # Basic UI components
47 β βββ forms/ # Form components
48 β
49 βββ _lib/ # Shared functions
50 β βββ api/ # API functions
51 β βββ hooks/ # Custom hooks
52 β βββ utils/ # Utility functions
53 β
54 βββ api/ # API endpoints
55 β βββ (public)/ # Public API
56 β βββ (protected)/ # Protected API
57 β
58 βββ layout.tsx # Main layoutRoute grouping and organization in Next.js 15 offers many possibilities for structuring applications in a logical and clear way, similar to how urban zoning in the Quantum Metropolis organizes urban space for maximum efficiency and functionality.
Key concepts:
Route groups (
(groupName)) - allow logical grouping of routes without affecting URL paths, which is particularly useful for dividing different application sections and implementing specific layouts.Private folders (
_folderName) - enable organization of code, shared components, and functions that are not directly accessible via URL.Hierarchical layouts - the ability to define specific layouts for different route groups, ensuring a consistent user experience within each section.
API organization - applying the same organizational principles to API endpoints.
With these techniques, you can create applications that are:
In the next chapter, we'll discover how to handle more complex navigation scenarios, such as nested routes and catch-all parameters, which enable creating advanced and flexible navigation systems in Next.js 15 applications.