RSC and Routing — Navigation, Params, and Route Handlers
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
Not handling missing params: Always validate that params exist before using them in database queries. Use notFound() for missing resources.
Forgetting searchParams is async: In Next.js 15+, searchParams is a Promise. Use await searchParams before accessing its properties.
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.
Not using route groups for shared layouts: Route groups prevent layout nesting from affecting the URL structure while sharing layouts across related routes.
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
- How do Server Components access route parameters?
Through the params prop. For a route like /products/[id], params.id contains the dynamic segment value.
- What is the difference between params and searchParams?
params contains dynamic route segments from the URL path. searchParams contains query string parameters.
- 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.
- 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.
- 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
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