React Router’s advanced features enable developers to create dynamic, scalable, and feature-rich single-page applications (SPAs). These advanced concepts include nested routes, route guards, lazy loading, data fetching, and more.
1. Nested Routes
Nested routes allow the definition of child routes within a parent route. This is particularly useful for dashboards or layouts with consistent navigation.
Example: Nested Routes
import React from "react"; import { BrowserRouter, Routes, Route, Outlet, Link } from "react-router-dom"; function Dashboard() { return ( <div> <h1>Dashboard</h1> <nav> <Link to="profile">Profile</Link> | <Link to="settings">Settings</Link> </nav> <Outlet /> </div> ); } function Profile() { return <h2>Profile Page</h2>; } function Settings() { return <h2>Settings Page</h2>; } function App() { return ( <BrowserRouter> <Routes> <Route path="/dashboard" element={<Dashboard />}> <Route path="profile" element={<Profile />} /> <Route path="settings" element={<Settings />} /> </Route> </Routes> </BrowserRouter> ); } export default App;
2. Lazy Loading with React Router
Lazy loading allows components to be loaded on demand, improving performance by reducing the initial bundle size.
Example: Lazy Loading
import React, { Suspense, lazy } from "react"; import { BrowserRouter, Routes, Route } from "react-router-dom"; const Home = lazy(() => import("./Home")); const About = lazy(() => import("./About")); function App() { return ( <BrowserRouter> <Suspense fallback={<div>Loading...</div>}> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> </Routes> </Suspense> </BrowserRouter> ); } export default App;
3. Route Guards
Route guards control access to routes based on conditions, such as user authentication.
Example: Route Guard
import React from "react"; import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; function ProtectedRoute({ element, isAuthenticated }) { return isAuthenticated ? element : <Navigate to="/login" />; } function App() { const isAuthenticated = false; // Example condition return ( <BrowserRouter> <Routes> <Route path="/login" element={<h1>Login Page</h1>} /> <Route path="/dashboard" element={<ProtectedRoute isAuthenticated={isAuthenticated} element={<h1>Dashboard</h1>} />} /> </Routes> </BrowserRouter> ); } export default App;
4. Dynamic Routing with Parameters
Dynamic routes enable passing and retrieving parameters through the URL.
Example: Route Parameters
import React from "react"; import { BrowserRouter, Routes, Route, useParams } from "react-router-dom"; function UserDetails() { const { userId } = useParams(); return <h1>User ID: {userId}</h1>; } function App() { return ( <BrowserRouter> <Routes> <Route path="/user/:userId" element={<UserDetails />} /> </Routes> </BrowserRouter> ); } export default App;
5. Programmatic Navigation
React Router provides the useNavigate
hook for programmatically navigating within the app.
Example: Programmatic Navigation
import React from "react"; import { BrowserRouter, Routes, Route, useNavigate } from "react-router-dom"; function Home() { const navigate = useNavigate(); return ( <div> <h1>Home Page</h1> <button onClick={() => navigate("/about")}>Go to About</button> </div> ); } function About() { return <h1>About Page</h1>; } function App() { return ( <BrowserRouter> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> </Routes> </BrowserRouter> ); } export default App;
6. Redirects
Redirects are used to navigate users to specific routes automatically.
Example: Redirect
import { Navigate } from "react-router-dom"; function App() { return ( <BrowserRouter> <Routes> <Route path="/" element={<Navigate to="/home" />} /> <Route path="/home" element={<h1>Home Page</h1>} /> </Routes> </BrowserRouter> ); }
7. Handling 404 Pages
Handle unmatched routes by defining a fallback route.
Example: 404 Route
function NotFound() { return <h1>404 - Page Not Found</h1>; } function App() { return ( <BrowserRouter> <Routes> <Route path="/" element={<h1>Home Page</h1>} /> <Route path="*" element={<NotFound />} /> </Routes> </BrowserRouter> ); }
8. Data Fetching in Routes
React Router can integrate data fetching to pre-load data before rendering a route.
Example: Fetch Data
import React, { useEffect, useState } from "react"; function Users() { const [users, setUsers] = useState([]); useEffect(() => { fetch("https://jsonplaceholder.typicode.com/users") .then((response) => response.json()) .then((data) => setUsers(data)); }, []); return ( <div> <h1>Users</h1> <ul> {users.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> </div> ); } export default Users;
9. BrowserRouter vs HashRouter
- BrowserRouter: Uses clean URLs (e.g.,
/about
). - HashRouter: Uses hash-based URLs (e.g.,
/#/about
).
Use HashRouter
for older browsers or environments where server configuration for clean URLs isn’t possible.
10. Best Practices
- Use lazy loading for large apps to optimize performance.
- Organize routes hierarchically using nested routes.
- Always include a 404 fallback route.
- Use programmatic navigation to enhance user experience.
- Secure sensitive routes using route guards.
- Leverage dynamic parameters for flexible routing.
React Router’s advanced features provide the flexibility to build robust, user-friendly applications with dynamic navigation.