Skip to content

RSC and Routing — Navigation, Params, and Route Handlers

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about RSC and Routing. We cover key concepts, practical examples, and best practices to help you master this topic.

Server Components work seamlessly with the App Router's routing system, accessing route parameters, search parameters, and integrating with route handlers for API endpoints.

What You'll Learn

You will understand how Server Components access route params and search params, how to handle redirects and not-found pages, and how route handlers complement Server Components.

Why It Matters

Routing is the backbone of any web application. Understanding how Server Components interact with routing enables building complex, data-driven pages with clean URL structures.

Real-World Use

DodaTech's tutorial platform uses dynamic route segments for lesson URLs, search params for filtering, and route handlers for the search API endpoint.

flowchart LR
    A[URL] --> B[Router]
    B --> C{Match Route}
    C --> D[Extract params]
    C --> E[Extract searchParams]
    D --> F[Server Component]
    E --> F
    F --> G[Fetch Data]
    G --> H[Render Page]
    style F fill:#1e293b,color:#fff
    style B fill:#0f172a,color:#fff

Dynamic Route Parameters

Server Components receive route parameters as props.

// app/products/[category]/[productId]/page.js
export default async function ProductPage({ params }) {
  const { category, productId } = params;
  const product = await db.products.findOne({
    category,
    id: productId
  });

  if (!product) {
    notFound();
  }

  return (
    <div>
      <h1>{product.name}</h1>
      <p>Category: {category}</p>
      <p>Price: ${product.price}</p>
      <p>Description: {product.description}</p>
    </div>
  );
}

Expected output: Accessing /products/electronics/123 renders the product detail page. The params object contains category=electronics and productId=123. If the product is not found, the not-found page displays.

Search Parameters

Server Components receive search parameters as a Promise that resolves to a URLSearchParams-like object.

// app/products/page.js
export default async function ProductsPage({ searchParams }) {
  const sp = await searchParams;
  const page = parseInt(sp.page) || 1;
  const sort = sp.sort || 'name';
  const category = sp.category || 'all';
  const search = sp.q || '';

  const filters = {};
  if (category !== 'all') filters.category = category;
  if (search) filters.name = { contains: search };

  const { products, totalPages } = await db.products.findPaginated({
    filters,
    sort,
    page,
    limit: 12
  });

  return (
    <div>
      <SearchSection initialQuery={search} />
      <FilterBar currentCategory={category} currentSort={sort} />
      <ProductGrid products={products} />
      <Pagination currentPage={page} totalPages={totalPages} />
    </div>
  );
}

Expected output: Accessing /products?page=2&sort=price&category=electronics&q=phone renders page 2 of electronics products sorted by price, filtered by search term phone.

Route Handlers

Route Handlers are the App Router equivalent of API Routes. They run on the server and complement Server Components.

// app/api/products/route.js
import { NextResponse } from 'next/server';

export async function GET(request) {
  const { searchParams } = new URL(request.url);
  const category = searchParams.get('category');
  const products = await db.products.findByCategory(category);
  return NextResponse.json(products);
}

export async function POST(request) {
  const body = await request.json();
  const product = await db.products.create(body);
  return NextResponse.json(product, { status: 201 });
}

Expected output: GET /api/products?category=electronics returns a JSON array of products. POST /api/products with a JSON body creates a product and returns it with a 201 status.

Redirects and Not Found

Server Components can trigger redirects and 404 responses.

import { redirect, notFound } from 'next/navigation';

export default async function AuthenticatedPage() {
  const user = await getCurrentUser();
  if (!user) {
    redirect('/login'); // Redirect to login
  }

  const data = await db.sensitiveData.findAll();
  if (!data || data.length === 0) {
    notFound(); // Show 404
  }

  return <Dashboard data={data} user={user} />;
}

Expected output: Unauthenticated users redirect to /login. If there is no data, the 404 page displays. Authenticated users with data see the dashboard.

Parallel Routes and Intercepting Routes

Advanced routing patterns work with Server Components for complex UIs.

// app/@modal/default.js — Parallel route for modals
export default function DefaultModal() {
  return null; // No modal by default
}

// app/@modal/(.)products/[id]/page.js — Intercepted route
export default async function ProductModal({ params }) {
  const product = await db.products.findById(params.id);
  return (
    <div style={{ position: 'fixed', ... }}>
      <h2>{product.name}</h2>
      <p>{product.description}</p>
      <Link href="/products">Close</Link>
    </div>
  );
}

Expected output: Clicking a product link from the products page opens a modal showing product details without navigating to a new page. The URL updates to the product's URL. Refreshing the page shows the full product page instead of the modal.

Common Mistakes

  1. Not handling missing params: Always validate that params exist before using them in database queries. Use notFound() for missing resources.

  2. Forgetting searchParams is async: In Next.js 15+, searchParams is a Promise. Use await searchParams before accessing its properties.

  3. Mixing up route handlers and Server Components: Route handlers return JSON responses for API clients. Server Components return HTML for page rendering. Use each for its purpose.

  4. Not using route groups for shared layouts: Route groups prevent layout nesting from affecting the URL structure while sharing layouts across related routes.

  5. Hardcoding URLs instead of using the Link component: Use next/link for client-side navigation. Server Components use standard anchor tags for static links but Link for client navigation.

Practice Questions

  1. How do Server Components access route parameters?

Through the params prop. For a route like /products/[id], params.id contains the dynamic segment value.

  1. What is the difference between params and searchParams?

params contains dynamic route segments from the URL path. searchParams contains query string parameters.

  1. When should you use a route handler instead of a Server Component?

Route handlers return JSON for API clients (mobile apps, third-party integrations). Server Components return HTML for page rendering.

  1. How do you trigger a 404 from a Server Component?

Call the notFound() function from next/navigation. It renders the closest not-found.js file.

  1. What are parallel routes used for?

Parallel routes render multiple pages simultaneously in the same layout, useful for modals, dashboards with multiple sections, and complex UIs.

Challenge

Build a product listing with parallel routes: the main area shows the product grid, and clicking a product opens a modal via intercepted route. The modal fetches and displays the product data. Refreshing the page navigates to the full product page.

Frequently Asked Questions

Can Server Components access headers?

Yes. Use the headers() function from next/headers inside Server Components to read request headers.

Can Server Components access cookies?

Yes. Use the cookies() function from next/headers inside Server Components to read and set cookies.

How do I handle form submissions across routes?

Use Server Actions with form action. After the action completes, use revalidatePath or redirect to navigate to the desired route.

Can I use middleware to protect Server Component routes?

Yes. Middleware runs before the route handler and can redirect unauthenticated users, preventing the Server Component from rendering.

How does the Router Cache affect Server Components?

The Router Cache stores rendered pages in the browser during client-side navigation. Server Components re-fetch data when the cache expires or is invalidated.

Mini Project

Build a searchable, filterable product catalog with Server Components handling all data fetching, route params for categories, search params for filters, and a route handler for the search API.

What's Next

Learn about RSC Performance to optimize your Server Component applications.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro