React Router: Routes, Route, and Link
Client-side routing is the foundation of modern single-page applications. React Router v6+ provides three core components—<Routes>, <Route>, and <Link>—that enable declarative URL-based navigation without full page reloads. This guide covers how these components work together to build responsive, multi-page UIs in React.
What Are the Core React Router Components?
React Router provides these three essential components for routing:
<Routes> is a container that matches the current URL against a list of <Route> definitions. It renders the first route whose path matches the URL. If no routes match, nothing is rendered. Think of it as a switch statement for URLs.
<Route> defines a single route by pairing a URL path (e.g., /about) with a component to render (e.g., <About />). When the URL matches the route's path, React Router renders that component.
<Link> creates clickable navigation links (similar to <a> tags) that change the URL without triggering a full page reload. This preserves component state and enables the smooth, fast experience users expect from single-page apps.
How Do You Define Routes in React Router?
Start by creating page components:
// Home.jsx
export default function Home() {
return <h1>Welcome Home</h1>;
}
// About.jsx
export default function About() {
return <h1>About Our App</h1>;
}
// Contact.jsx
export default function Contact() {
return <h1>Contact Us</h1>;
}
Then define routes in your App.jsx:
import { Routes, Route } from 'react-router-dom';
import Home from './Home';
import About from './About';
import Contact from './Contact';
export default function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
);
}
When the browser URL is /about, React Router matches it to the second route and renders <About />. Each route has a path (the URL to match) and an element (the component to display).
How Do You Add Navigation with Link?
Use <Link> components to navigate between routes without full page reloads:
import { Routes, Route, Link } from 'react-router-dom';
import Home from './Home';
import About from './About';
import Contact from './Contact';
export default function App() {
return (
<div>
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/contact">Contact</Link></li>
</ul>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</div>
);
}
The to prop specifies where to navigate. Clicking a <Link> updates the URL and renders the matching route's component. No page refresh occurs; component state is preserved across navigation.
Key Differences: Link vs. Anchor Tags
<Link> components render as <a> tags but intercept clicks to prevent default browser behavior (full page reload). Instead, React Router updates the URL in the browser history and re-renders the matching route.
Use <Link> for internal app navigation. Use <a> only for external URLs:
// Internal navigation — use Link
<Link to="/about">About Page</Link>
// External link — use <a>
<a href="https://example.com">External Site</a>
Organizing Routes for Scalability
For larger apps, organize routes in a configuration file:
// routes.js
import Home from './pages/Home';
import About from './pages/About';
import Contact from './pages/Contact';
export const routes = [
{ path: '/', element: <Home /> },
{ path: '/about', element: <About /> },
{ path: '/contact', element: <Contact /> },
];
// App.jsx
import { Routes, Route } from 'react-router-dom';
import { routes } from './routes';
export default function App() {
return (
<Routes>
{routes.map((route) => (
<Route key={route.path} path={route.path} element={route.element} />
))}
</Routes>
);
}
This pattern scales to dozens of routes without making App.jsx unwieldy.
Handling Unmatched Routes
Use a catch-all route with path="*" to handle URLs that don't match any defined route:
import NotFound from './pages/NotFound';
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="*" element={<NotFound />} />
</Routes>
The * route must come last because <Routes> matches in order. If it's first, it will match every URL.
Best Practices for React Router
Place BrowserRouter High in the Tree: Wrap your app with <BrowserRouter> near the root (in main.jsx or a layout component) so all routes and navigation links can access router context.
Keep Routes and Navigation Together: Define routes and navigation in the same component or a shared layout. This keeps the routing structure visible and maintainable.
Use Semantic Path Names: Keep URLs descriptive and lowercase. /user-profile is clearer than /up or /userprofile123.
Nest Routes Logically: Group related routes together. All admin routes could be under /admin/*, all blog routes under /blog/*.
Anti-Patterns to Avoid
Do Not Manually Change the URL: Never modify window.location or window.history directly. Let React Router manage the URL via <Link> or the useNavigate hook.
Do Not Forget to Wrap Your App with BrowserRouter: Routes and navigation won't work without <BrowserRouter> at the root level.
Do Not Use Anchor Tags for Internal Navigation: Using <a href="/about"> causes a full page reload and loses all component state. Always use <Link>.
Key Takeaways
<Routes>is a container that matches the current URL against<Route>definitions and renders the first matching route.<Route>pairs a URL path with a component; when the URL matches the path, that component renders.<Link>provides navigation between routes without full page reloads, preserving app state and enabling the smooth single-page app experience.<Link>uses thetoprop (instead ofhref) to specify the target path.- A catch-all
path="*"route handles unmatched URLs; place it last in your<Routes>list.
Frequently Asked Questions
What is the difference between a <Route> and a <Link>?
<Route> defines which component renders for a given URL path. <Link> is a clickable element that navigates to a path. Routes define what; Links trigger navigation.
Why does my <Link> not work?
Ensure your entire app is wrapped in <BrowserRouter> (typically in main.jsx). <Link> components can only work within a router context. If BrowserRouter is missing, clicks do nothing.
Can I have multiple <Routes> in my app?
Yes. Each <Routes> independently matches against the current URL. You might have one main <Routes> for pages and another for modals or sidebars. Each renders matching components independently.
How do I pass data to a routed component?
Use URL parameters (/user/:id) to encode identifiers, then access them with useParams(). For complex data, lift state to a parent component or use a state management library like Redux.
What happens if two routes have the same path?
The first matching route wins. <Routes> stops at the first match, so order matters. More specific routes (e.g., /user/profile) should come before generic ones (e.g., /user/:id).