DEV Community

Cover image for Advanced Routing Techniques in Next.js
Rowsan Ali
Rowsan Ali

Posted on

Advanced Routing Techniques in Next.js

Next.js 13+ introduced revolutionary changes to routing with the App Router. In this comprehensive guide, we'll explore advanced routing techniques that will help you build more sophisticated and maintainable Next.js applications.

Table of Contents

  1. Understanding the App Router
  2. Dynamic Routes and Segments
  3. Parallel and Intercepting Routes
  4. Route Groups and Organization
  5. Loading and Error States
  6. Route Handlers (API Routes)
  7. Middleware and Protected Routes

Understanding the App Router

The App Router works on the convention of using directories for routing. Here's the basic structure:

app/
├── page.tsx          // Home route (/)
├── about/
   └── page.tsx      // About route (/about)
├── blog/
   └── page.tsx      // Blog route (/blog)
   └── [slug]/
       └── page.tsx  // Individual blog post (/blog/post-1)
Enter fullscreen mode Exit fullscreen mode

Dynamic Routes and Segments

Dynamic Segments

Dynamic segments allow you to create routes with parameters. Here's how to implement them:

// app/blog/[slug]/page.tsx
export default async function BlogPost({ params }: { params: { slug: string } }) {
  return (
    <article>
      <h1>Blog Post: {params.slug}</h1>
    </article>
  );
}

// Generate static params for static generation
export async function generateStaticParams() {
  const posts = await fetchPosts();

  return posts.map((post) => ({
    slug: post.slug,
  }));
}
Enter fullscreen mode Exit fullscreen mode

Catch-all Segments

For handling multiple dynamic segments:

// app/docs/[...slug]/page.tsx
export default function DocsPage({ params }: { params: { slug: string[] } }) {
  // slug will be an array: /docs/a/b/c → ['a', 'b', 'c']
  return (
    <div>
      <h1>Documentation</h1>
      <p>Current path: {params.slug.join('/')}</p>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Parallel and Intercepting Routes

Parallel Routes

Parallel routes allow you to simultaneously render multiple pages in the same layout:

// app/@modal/login/page.tsx
export default function LoginModal() {
  return (
    <div className="modal">
      <h2>Login</h2>
      {/* Login form */}
    </div>
  );
}

// app/layout.tsx
export default function Layout(props: {
  children: React.ReactNode;
  modal: React.ReactNode;
}) {
  return (
    <div>
      {props.children}
      {props.modal}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Intercepting Routes

Intercepting routes let you show a route while preserving the context of the current page:

// app/photos/[...slug]/(..)gallery/page.tsx
// This will intercept /photos/gallery while showing /photos/[slug]
export default function GalleryModal() {
  return (
    <div className="modal">
      <h2>Photo Gallery</h2>
      {/* Gallery content */}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Route Groups and Organization

Route groups help organize your routes without affecting the URL structure:

app/
├── (marketing)
   ├── about/
   ├── blog/
   └── layout.tsx
├── (shop)
   ├── products/
   ├── cart/
   └── layout.tsx
└── layout.tsx
Enter fullscreen mode Exit fullscreen mode

Loading and Error States

Implement loading and error states for better user experience:

// app/products/loading.tsx
export default function Loading() {
  return (
    <div className="loading-spinner">
      <span>Loading...</span>
    </div>
  );
}

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

export default function Error({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  return (
    <div className="error-container">
      <h2>Something went wrong!</h2>
      <button onClick={() => reset()}>Try again</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Route Handlers (API Routes)

Create API endpoints using Route Handlers:

// 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 data = await request.json();
  const newPost = await createPost(data);
  return NextResponse.json(newPost, { status: 201 });
}
Enter fullscreen mode Exit fullscreen mode

Middleware and Protected Routes

Implement route protection using middleware:

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

export function middleware(request: NextRequest) {
  const token = request.cookies.get('auth-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*', '/api/:path*'],
};
Enter fullscreen mode Exit fullscreen mode

Best Practices and Tips

  1. Keep Routes Organized: Use route groups to maintain a clean project structure
  2. Implement Error Boundaries: Always handle errors gracefully
  3. Use Loading States: Provide feedback during data fetching
  4. Type Safety: Leverage TypeScript for better type safety
  5. Caching Strategy: Implement proper caching strategies using Next.js built-in caching mechanisms

Conclusion

Next.js's App Router provides powerful routing capabilities that can help you build complex applications with ease. By understanding and implementing these advanced routing techniques, you can create more maintainable and user-friendly applications.

Remember to:

  • Plan your route structure carefully
  • Implement proper error handling
  • Use TypeScript for better type safety
  • Consider performance implications
  • Keep your code organized using route groups

For more information, refer to the official Next.js documentation.


Note: This blog post assumes you're using Next.js 13+ with the App Router. Some features might vary depending on your Next.js version.

Top comments (0)