20 minLesson 20 of 40
React Fundamentals
React Router v6
React Router v6
React Router enables client-side navigation in single-page applications. Instead of the browser requesting a new HTML page, React Router swaps out components based on the URL — making navigation instant.
Setup
npm install react-router-dom
// main.jsx — wrap your app in BrowserRouter
import { BrowserRouter } from "react-router-dom";
ReactDOM.createRoot(document.getElementById("root")).render(
<BrowserRouter>
<App />
</BrowserRouter>
);
Defining Routes
import { Routes, Route } from "react-router-dom";
function App() {
return (
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/about" element={<AboutPage />} />
<Route path="/products" element={<ProductsPage />} />
<Route path="/products/:id" element={<ProductDetailPage />} />
<Route path="/dashboard" element={<DashboardLayout />}>
{/* Nested routes */}
<Route index element={<DashboardHome />} />
<Route path="settings" element={<Settings />} />
<Route path="analytics" element={<Analytics />} />
</Route>
<Route path="*" element={<NotFoundPage />} />
</Routes>
);
}
Navigation
import { Link, NavLink, useNavigate } from "react-router-dom";
// Link — declarative navigation
<Link to="/about">About</Link>
<Link to="/products/42">View Product</Link>
<Link to={{ pathname: "/search", search: "?q=react" }}>Search React</Link>
// NavLink — adds active class automatically
<NavLink
to="/dashboard"
className={({ isActive }) => isActive ? "nav-link active" : "nav-link"}
>
Dashboard
</NavLink>
// useNavigate — programmatic navigation
function LoginForm() {
const navigate = useNavigate();
async function handleSubmit(e) {
e.preventDefault();
await login(form);
navigate("/dashboard"); // redirect
navigate("/dashboard", { replace: true }); // replace history entry
navigate(-1); // go back
navigate(1); // go forward
}
}
URL Parameters
import { useParams } from "react-router-dom";
// Route: /products/:id
function ProductDetailPage() {
const { id } = useParams();
const [product, setProduct] = useState(null);
useEffect(() => {
fetch(`/api/products/${id}`).then(r => r.json()).then(setProduct);
}, [id]);
if (!product) return <p>Loading...</p>;
return <div>{product.name}</div>;
}
Search Params
import { useSearchParams } from "react-router-dom";
// URL: /products?category=electronics&sort=price
function ProductsPage() {
const [searchParams, setSearchParams] = useSearchParams();
const category = searchParams.get("category") ?? "all";
const sort = searchParams.get("sort") ?? "name";
function handleCategoryChange(newCategory) {
setSearchParams(prev => {
const next = new URLSearchParams(prev);
next.set("category", newCategory);
return next;
});
}
// ...
}
Nested Routes with Outlet
// Layout component renders child routes
function DashboardLayout() {
return (
<div className="dashboard">
<nav>
<NavLink to="/dashboard">Home</NavLink>
<NavLink to="/dashboard/settings">Settings</NavLink>
<NavLink to="/dashboard/analytics">Analytics</NavLink>
</nav>
<main>
<Outlet /> {/* child route renders here */}
</main>
</div>
);
}
Protected Routes
function RequireAuth({ children }) {
const { user } = useAuth();
const location = useLocation();
if (!user) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
return children;
}
// Usage
<Route
path="/dashboard"
element={<RequireAuth><DashboardLayout /></RequireAuth>}
>
Next lesson: Tailwind CSS — utility-first styling that makes design fast.
📱
Get Notes Free →Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises