React Router
A single-page app still needs real URLs: /products, /products/7, /cart. React Router maps URLs to components, so the back button, bookmarks and shared links all work.
On this page · 6 min · Intermediate
On this page
- Learning objectives
- Explanation
- Syntax
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 6 min
- Difficulty
- Intermediate
- Updated
- Oct 28, 2026
Related tutorials
Learning objectives#
- Define routes with createBrowserRouter
- Build layouts with nested routes and <Outlet />
- Navigate with Link, NavLink and useNavigate
- Read URL and query parameters
Explanation#
Install react-router (v7). createBrowserRouter([...]) lists routes: { path: "/", element: <Layout />, children: [...] }. Render it with <RouterProvider router={router} />.
Nested routes share a layout: the parent renders the header and footer plus <Outlet /> where the matching child appears. { index: true } is the child for the parent’s own URL.
<Link to="/cart"> changes the URL without reloading the page. <NavLink> adds an active class for the current page. useNavigate() navigates from code, e.g. after saving.
Dynamic segments: path "products/:id"; read with const { id } = useParams() (always a string).
Query strings: const [searchParams, setSearchParams] = useSearchParams(); searchParams.get("q"). A path "*" catches unknown URLs for a 404 page.
On the server, configure a fallback so every URL returns index.html (see the deployment lesson).
Syntax#
const router = createBrowserRouter([
{ path: '/', element: <Layout />, children: [
{ index: true, element: <Home /> },
{ path: 'products/:id', element: <ProductPage /> },
{ path: '*', element: <NotFound /> },
]},
]);
<RouterProvider router={router} />
Code example: A small shop with nested routes#
import { createRoot } from 'react-dom/client';
import {
createBrowserRouter, RouterProvider, Outlet, Link, NavLink,
useParams, useNavigate, useSearchParams,
} from 'react-router';
const PRODUCTS = [{ id: '1', name: 'Pen', price: 20 }, { id: '2', name: 'Notebook', price: 60 }];
function Layout() {
return (
<>
<nav>
<NavLink to="/" end>Home</NavLink> | <NavLink to="/products">Products</NavLink>
</nav>
<main><Outlet /></main>
</>
);
}
function Home() {
return <h1>Welcome to the shop</h1>;
}
function ProductList() {
const [searchParams, setSearchParams] = useSearchParams();
const q = searchParams.get('q') ?? '';
const visible = PRODUCTS.filter(p => p.name.toLowerCase().includes(q.toLowerCase()));
return (
<>
<input value={q} onChange={e => setSearchParams({ q: e.target.value })} aria-label="Search" />
<ul>{visible.map(p => <li key={p.id}><Link to={`/products/${p.id}`}>{p.name}</Link></li>)}</ul>
</>
);
}
function ProductPage() {
const { id } = useParams();
const navigate = useNavigate();
const product = PRODUCTS.find(p => p.id === id);
if (!product) return <p>Product not found.</p>;
return (
<>
<h2>{product.name} — ₹{product.price}</h2>
<button onClick={() => navigate('/products')}>Back to list</button>
</>
);
}
function NotFound() {
return <p>404 — page not found. <Link to="/">Go home</Link></p>;
}
const router = createBrowserRouter([
{
path: '/',
element: <Layout />,
children: [
{ index: true, element: <Home /> },
{ path: 'products', element: <ProductList /> },
{ path: 'products/:id', element: <ProductPage /> },
{ path: '*', element: <NotFound /> },
],
},
]);
createRoot(document.getElementById('root')).render(<RouterProvider router={router} />);
“/” shows the nav and “Welcome to the shop”. “/products” lists Pen and Notebook; typing in the search box updates the URL to /products?q=note and filters the list. Clicking Notebook opens /products/2 with “Notebook — ₹60” and a Back button. Any unknown URL shows the 404 message. The nav is shared by every page and the active link is highlighted.
This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.
Step-by-step explanation#
- The router lists one parent route (Layout) with four children.
- Layout renders the shared nav and an <Outlet /> where the child page appears.
- NavLink marks the current page as active; end makes “Home” active only on “/”.
- useSearchParams keeps the search text in the URL, so it can be bookmarked and shared.
- useParams reads :id from /products/2; useNavigate goes back to the list from code.
- path "*" catches every unknown URL.
Real-world use case#
An admin dashboard has /orders, /orders/:id, /customers and /settings under one layout with a sidebar. Support staff paste links like /orders/1042 into tickets, and they open the right page directly.
Common mistakes#
A normal anchor reloads the whole app and loses state. Use <Link to>.
The server looks for a real /products/2 file. Configure a fallback to index.html.
useParams() always returns strings. Convert with Number(id) or compare as strings.
Best practices#
- Shared layouts with nested routes and Outlet
- Keep filters and pages in the URL (search params)
- A catch-all 404 route
- Lazy-load large pages per route
Remember this#
Route → element. Parent layout → <Outlet /> for the child.
<Link to> navigates without reloading.
useParams for /:id, useSearchParams for ?q=.
Practice exercise#
Your task: Add a route /orders/:orderId that shows "Order #<orderId>" and a link back to /orders.
Need a hint?
Show solution
import { Link, useParams } from 'react-router';
export function OrderPage() {
const { orderId } = useParams();
return (
<>
<h2>Order #{orderId}</h2>
<Link to="/orders">← All orders</Link>
</>
);
}
// In the router's children: { path: 'orders/:orderId', element: <OrderPage /> }
Mini quiz#
Where does a nested child route render inside its parent layout?
Want more? Practise all React MCQs · React interview questions
My notes#
Saved in this browser.