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.