logo
Back to Blog
Next.js 15/16 Routing Guide 2026
frontenddevelopmentnextjsreacttypescriptapp-router

Next.js 15/16 Routing Guide 2026

Learn modern routing in Next.js 15 and 16 with App Router, dynamic routes, and essential routing patterns for 2026.

Next.js 15/16 Routing Guide

Next.js App Router brings a powerful file-system-based routing system built on React Server Components and React 19. This guide covers the essential routing patterns in Next.js 15/16.

What's New in Next.js 15/16

Key Features:

  • Turbopack (Stable) - Lightning-fast development bundler
  • Async Request APIs - params and searchParams now async
  • Cache Components - New use cache directive
  • React 19 - Full support with Server Components
  • Enhanced Routing - Optimized navigation and prefetching

App Directory Structure

The app directory is the foundation of Next.js routing. Every folder becomes a route, and special files define behavior:

app/
├── layout.tsx          # Root layout (required)
├── page.tsx            # Homepage route
├── loading.tsx         # Loading UI
├── error.tsx           # Error handling
└── not-found.tsx       # 404 page

File Hierarchy: layout → error → loading → page

Basic Routing

Every folder with a page.tsx becomes a route.

Example: app/about/page.tsx

export default function AboutPage() {
  return <h1>About Us</h1>;
}

URL: /about

Folder Structure

app/
├── page.tsx           # /
├── about/
   └── page.tsx       # /about
└── blog/
    └── page.tsx       # /blog

Dynamic Routes

Use brackets [param] for dynamic URL segments.

File: app/products/[id]/page.tsx

interface PageProps {
  params: Promise<{ id: string }>;
}

export default async function ProductPage({ params }: PageProps) {
  const { id } = await params;
  return <h1>Product {id}</h1>;
}

URLs: /products/1, /products/laptop, etc.

Important: In Next.js 15+, params and searchParams are async!

Catch-All Routes

Match multiple segments with [...slug]:

File: app/docs/[...slug]/page.tsx

interface PageProps {
  params: Promise<{ slug: string[] }>;
}

export default async function DocsPage({ params }: PageProps) {
  const { slug } = await params;
  return <p>Path: /{slug.join('/')}</p>;
}

Matches: /docs/api/referenceslug = ['api', 'reference']

Optional Catch-All [[...slug]]

Use double brackets to also match the parent route:

File: app/docs/[[...slug]]/page.tsx

Matches: /docs AND /docs/api/reference

Route Groups

Organize routes without affecting URLs using (folder):

app/
├── (marketing)/
   ├── page.tsx          # /
   └── about/
       └── page.tsx      # /about
└── (dashboard)/
    └── dashboard/
        └── page.tsx      # /dashboard

Each group can have its own layout.tsx with different styles/navigation.

Parallel Routes

Render multiple pages in the same layout using @folder slots:

app/dashboard/
├── layout.tsx
├── @team/page.tsx
└── @analytics/page.tsx
// layout.tsx
export default function Layout({
  children,
  team,
  analytics,
}: {
  children: React.ReactNode;
  team: React.ReactNode;
  analytics: React.ReactNode;
}) {
  return (
    <>
      {children}
      <aside>
        {team}
        {analytics}
      </aside>
    </>
  );
}

Intercepting Routes

Show modals without changing the URL using route interception:

  • (.) - Same level
  • (..) - One level up
  • (...) - From root

Example: Photo modal

app/photos/
├── page.tsx              # Grid
├── [id]/page.tsx         # Full page
└── (..)photos/[id]/page.tsx  # Modal

Click from grid → shows modal. Refresh → shows full page.

Layouts and Error Handling

Root Layout (Required)

// app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

Loading States

// app/dashboard/loading.tsx
export default function Loading() {
  return <div>Loading...</div>;
}

Error Boundaries

// app/dashboard/error.tsx
'use client';

export default function Error({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  return (
    <div>
      <h2>Something went wrong!</h2>
      <button onClick={reset}>Try again</button>
    </div>
  );
}

Not Found

// app/not-found.tsx
export default function NotFound() {
  return <h2>Page Not Found</h2>;
}

Navigation

Link Component

import Link from 'next/link';

<Link href="/about">About</Link>
<Link href="/blog/post-1">First Post</Link>
<Link href="/products?sort=price">Products</Link>

Programmatic Navigation

'use client';
import { useRouter } from 'next/navigation';

export default function LoginButton() {
  const router = useRouter();
  return <button onClick={() => router.push('/dashboard')}>Login</button>;
}

Hooks

'use client';
import { usePathname, useSearchParams } from 'next/navigation';

const pathname = usePathname(); // /blog/post-1
const searchParams = useSearchParams(); // ?sort=price
const sort = searchParams.get('sort'); // 'price'

Metadata

Static Metadata

import { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'Blog Posts',
  description: 'Read our latest articles',
};

Dynamic Metadata

export async function generateMetadata({ 
  params 
}: { 
  params: Promise<{ slug: string }> 
}): Promise<Metadata> {
  const { slug } = await params;
  const post = await fetchPost(slug);
  return {
    title: post.title,
    description: post.excerpt,
  };
}

API Routes

Create API endpoints with route.ts:

// app/api/posts/route.ts
import { NextResponse } from 'next/server';

export async function GET() {
  const posts = await fetchPosts();
  return NextResponse.json(posts);
}

export async function POST(request: Request) {
  const body = await request.json();
  const post = await createPost(body);
  return NextResponse.json(post, { status: 201 });
}

Dynamic API Routes

// app/api/posts/[id]/route.ts
export async function GET(
  request: Request,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  const post = await fetchPost(id);
  return NextResponse.json(post);
}

Static Generation

Pre-render dynamic routes at build time:

// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await fetchAllPosts();
  return posts.map((post) => ({
    slug: post.slug,
  }));
}

export default async function PostPage({ 
  params 
}: { 
  params: Promise<{ slug: string }> 
}) {
  const { slug } = await params;
  const post = await fetchPost(slug);
  return <article>{post.content}</article>;
}

Middleware

Run code before requests complete:

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

export function middleware(request: NextRequest) {
  const token = request.cookies.get('token');

  if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ['/dashboard/:path*'],
};

Best Practices

  1. Use Server Components by default - Only add 'use client' when needed
  2. Async params - Always await params and searchParams in Next.js 15+
  3. Loading states - Add loading.tsx for better UX
  4. Error boundaries - Use error.tsx to handle errors gracefully
  5. Route groups - Organize routes with (folder) without affecting URLs
  6. Static generation - Use generateStaticParams for pre-rendering
  7. Metadata - Always add proper SEO metadata

Key Takeaways

  • File-system routing - Folders create routes, page.tsx makes them public
  • Dynamic routes - Use [param] for dynamic segments
  • Catch-all - [...slug] for multi-segment paths
  • Async params - Required in Next.js 15+
  • Server Components - Default and recommended
  • Turbopack - Fast development with stable bundler

Resources

Related Posts

Next.js 15/16 Data Fetching: Mistakes & Security Issues

Next.js 15/16 Data Fetching: Mistakes & Security Issues

Learn about common Next.js data fetching mistakes and security vulnerabilities with solutions for Next.js 15/16.

frontenddevelopmentnextjs+1 more
Read More
TypeScript for Frontend Development: A Beginner's Guide

TypeScript for Frontend Development: A Beginner's Guide

A guide to TypeScript for frontend development for beginners part 1

frontenddevelopmenttypescript
Read More