React Router enables Single Page Application (SPA) navigation. Instead of reloading the page, it updates the URL and swaps components dynamically.
The classic routing setup (before createBrowserRouter) relies on three core components: BrowserRouter, Routes, and Route.
BrowserRouter uses the browser’s History API (pushState, popstate) to change URLs without refreshing.
// Root-level router wrapper
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
ReactDOM.createRoot(document.getElementById("root")).render(
<BrowserRouter>
<App />
</BrowserRouter>
);
In React Router v6, Routes replaces Switch. Only the best matching route is rendered.
// Each Route maps a path to a JSX element
import { Routes, Route } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";
import Contact from "./pages/Contact";
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
);
}
export default App;
Nested routes allow layouts like dashboards where child pages render inside a parent UI.
// Nested routes render inside parent outlet
<Routes>
<Route path="/dashboard" element={<Dashboard />}>
<Route path="profile" element={<Profile />} />
<Route path="settings" element={<Settings />} />
</Route>
</Routes>
Route parameters allow URLs like /users/42.
// :id captures dynamic values from URL
<Route path="/users/:id" element={<UserDetails />} />
A wildcard * path catches unmatched URLs.
// Catch-all route for unknown paths
<Route path="*" element={<NotFound />} />
Link prevents page reloads and keeps SPA behavior intact.
// Client-side navigation
import { Link } from "react-router-dom";
function Navbar() {
return (
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/contact">Contact</Link>
</nav>
);
}
BrowserRouter once at the rootLink over <a>/blog/:slug route