Routing & Navigation
Routing is essential for creating navigable React applications. This guide covers React Router fundamentals, private route protection, and navigation patterns.
React Router Setup
Section titled “React Router Setup”Installation
Section titled “Installation”npm install react-router-dom
Basic Configuration
Section titled “Basic Configuration”// App.jsimport { BrowserRouter, Routes, Route } from 'react-router-dom';import HomePage from './pages/HomePage';import AboutPage from './pages/AboutPage';import ProductPage from './pages/ProductPage';import NotFoundPage from './pages/NotFoundPage';
function App() { return ( <BrowserRouter> <Routes> <Route path="/" element={<HomePage />} /> <Route path="/about" element={<AboutPage />} /> <Route path="/products/:id" element={<ProductPage />} /> <Route path="*" element={<NotFoundPage />} /> </Routes> </BrowserRouter> );}
export default App;
Navigation Components
Section titled “Navigation Components”Basic Navigation
Section titled “Basic Navigation”Navigation in React Router uses special components that prevent full page reloads and provide better user experience.
// components/Navigation.jsximport { Link, NavLink } from 'react-router-dom';
const Navigation = () => { return ( <nav> {/* Link: Basic navigation without active state */} <Link to="/">Home</Link>
{/* NavLink: Automatically adds 'active' class when current */} <NavLink to="/about" className={({ isActive }) => isActive ? 'active' : ''} > About </NavLink>
<NavLink to="/products">Products</NavLink> </nav> );};
Key Differences:
Link
: Basic navigation, no active state indicationNavLink
: Automatically knows when it’s the current page, useful for navigation menus
Programmatic Navigation
Section titled “Programmatic Navigation”Sometimes you need to navigate based on user actions like form submissions or button clicks.
// components/LoginForm.jsximport { useNavigate } from 'react-router-dom';
const LoginForm = () => { const navigate = useNavigate();
const handleLogin = async (credentials) => { try { await authService.login(credentials); navigate('/dashboard'); // Redirect after successful login } catch (error) { console.error('Login failed:', error); } };
const goBack = () => { navigate(-1); // Go back one page in browser history };
return ( <form onSubmit={handleLogin}> {/* form content */} <button type="button" onClick={goBack}> Back </button> </form> );};
Key Points:
useNavigate()
returns a function for programmatic navigationnavigate('/path')
goes to a specific routenavigate(-1)
goes back in historynavigate(1)
goes forward in history
Protected Routes
Section titled “Protected Routes”For implementing authentication and protecting routes, see the Authentication & Security guide which covers:
- Setting up authentication context
- Protected route components with role-based access control
- Login/logout flows
- JWT token management
Dynamic Routing & Parameters
Section titled “Dynamic Routing & Parameters”URL Parameters with useParams
Section titled “URL Parameters with useParams”Dynamic routes allow you to create flexible URLs that can handle variable segments. The useParams
hook extracts these parameters from the URL.
// pages/ProductPage.jsximport { useParams, useNavigate } from 'react-router-dom';import { useState, useEffect } from 'react';
const ProductPage = () => { const { id } = useParams(); // Extract 'id' from /products/:id const navigate = useNavigate(); const [product, setProduct] = useState(null); const [loading, setLoading] = useState(true);
useEffect(() => { const fetchProduct = async () => { try { const data = await productService.getById(id); setProduct(data); } catch (error) { console.error('Product not found:', error); navigate('/products'); // Redirect if not found } finally { setLoading(false); } };
fetchProduct(); }, [id, navigate]); // Re-run when ID changes
if (loading) return <div>Loading...</div>; if (!product) return <div>Product not found</div>;
return ( <div> <h1>{product.name}</h1> <p>{product.description}</p> <p>Price: ${product.price}</p> </div> );};
Key Points:
:id
in the route becomes accessible viauseParams()
- Always handle loading and error states
- Use
useEffect
with the parameter as a dependency
Query Parameters with useSearchParams
Section titled “Query Parameters with useSearchParams”Query parameters handle optional data like filters, search terms, and pagination. For complex state management with search parameters, see our State Management guide.
Nested Routes
Section titled “Nested Routes”// App.js - Nested route structure<Routes> <Route path="/products" element={<ProductLayout />}> <Route index element={<ProductList />} /> <Route path=":id" element={<ProductDetail />} /> <Route path=":id/edit" element={<ProductEdit />} /> </Route></Routes>
// components/ProductLayout.jsximport { Outlet } from 'react-router-dom';
const ProductLayout = () => { return ( <div> <h1>Products</h1> <nav> <Link to="/products">All Products</Link> </nav> <main> <Outlet /> {/* Child routes render here */} </main> </div> );};
Best Practices
Section titled “Best Practices”Route Organization
Section titled “Route Organization”- Keep Routes Flat: Avoid deeply nested route structures
- Use Layout Components: Share common UI with Outlet
- Lazy Loading: Use React.lazy for code splitting
- Error Handling: Always handle route errors gracefully
Navigation Patterns
Section titled “Navigation Patterns”- Use NavLink for Active States: Better UX with visual feedback
- Preserve Query Parameters: Maintain filters and pagination
- Handle Loading States: Show loading indicators during navigation
- Breadcrumbs: Help users understand their location
Security
Section titled “Security”- Client-Side Only: Remember that React Router is client-side
- Server Validation: Always validate permissions on the server
- Redirect Safely: Avoid open redirects with user input
- Clean URLs: Use meaningful, SEO-friendly routes