React Router v6 is a significant update for route management in React applications, introducing improvements and simplifications, including better handling of nested routes and enhanced use of hooks.
1. Routes Refactor
In v6, the <Route>
component is wrapped within a <Routes>
component, replacing the <Switch>
component. <Routes>
automatically renders the first matching route.
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/users/*" element={<Users />} /> {/* Wildcard support */}
</Routes>
</Router>
);
}
2. Using the element
Property
Routes now specify the component to render using the element
property, replacing component
or render
functions.
<Route path="/profile/:userId" element={<Profile />} />
3. Hook API
React Router v6 introduces hooks like useParams
, useLocation
, and useNavigate
, enabling direct navigation handling within components.
import { useParams } from 'react-router-dom';
function Profile() {
const { userId } = useParams();
return <div>Profile of user {userId}</div>;
}
4. Navigate Function
The useNavigate
hook returns a navigate
function for programmatic navigation.
import { useNavigate } from 'react-router-dom';
function ProfileForm() {
const navigate = useNavigate();
const handleSubmit = (event) => {
event.preventDefault();
// Navigate to another page after form submission
navigate('/success', { replace: true }); // Replace current history entry
};
return <form onSubmit={handleSubmit}>...</form>;
}
5. Lazy Loading and Code Splitting
React Router v6 supports dynamic imports for code splitting, improving load times.
<Route path="/lazy" element={import('./LazyComponent').then((mod) => mod.LazyComponent)} />
6. 404 Page
Handle unmatched routes with a wildcard route to display a 404 page.
<Route path="*" element={<Error404 />} />
7. Nested Routes
Nested routes are simplified using <Routes>
and <Route>
combinations.
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<MainLayout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="users" element={<Users />}>
<Route path=":userId" element={<User />} />
</Route>
</Route>
<Route path="*" element={<Error404 />} />
</Routes>
</Router>
);
}
8. Route Protection and Authorization
Use useEffect
or useLayoutEffect
with useNavigate
to protect routes, ensuring only authenticated users access them.
import { useNavigate, useLocation } from 'react-router-dom';
function PrivateRoute({ children }) {
const location = useLocation();
const navigate = useNavigate();
useEffect(() => {
if (!isAuthenticated()) {
navigate('/login', { replace: true });
}
}, [navigate]);
return isAuthenticated() ? children : null;
}
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<PublicRoute><Home /></PublicRoute>} />
<Route path="/dashboard" element={<PrivateRoute><Dashboard /></PrivateRoute>} />
<Route path="/login" element={<Login />} />
</Routes>
</Router>
);
}
Here, isAuthenticated()
is a hypothetical function checking user authentication. Unauthenticated users are redirected to the login page.
9. Redirects and Navigation
The <Navigate>
component facilitates redirects by triggering navigation to a specified URL.
import { Navigate } from 'react-router-dom';
function PrivateRoute({ children }) {
if (!isAuthenticated()) {
return <Navigate to="/login" replace />;
}
return children;
}
10. Route Parameters and Query Strings
Use useParams
for route parameters and useLocation
for query strings.
import { useParams, useLocation } from 'react-router-dom';
function Profile() {
const { userId } = useParams();
const query = new URLSearchParams(useLocation().search);
const searchParam = query.get('paramName');
return <div>Profile of user {userId} with search param: {searchParam}</div>;
}
11. Custom Route Matching
React Router v6 supports custom route matching via the path-to-regexp
library, though standard path patterns are usually sufficient.
12. Higher-Order Components (HoCs)
While v6 discourages HoCs, you can still wrap components for specific logic, such as route protection.
function PrivateRoute({ children }) {
if (!isAuthenticated()) {
return <Navigate to="/login" replace />;
}
return <>{children}</>;
}
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<PublicRoute><Home /></PublicRoute>} />
<Route path="/dashboard" element={<PrivateRoute><Dashboard /></PrivateRoute>} />
<Route path="/login" element={<Login />} />
</Routes>
</Router>
);
}
13. Route Events
React Router v6 provides lifecycle hooks like useLocation
and useNavigate
to monitor and respond to route changes.
import { useLocation } from 'react-router-dom';
function Navbar() {
const location = useLocation();
useEffect(() => {
console.log(`Navigated to ${location.pathname}`);
}, [location]);
return /* ... */;
}
14. Modular Route Configuration
Keep code clean by splitting route configurations into modules and merging them in the main configuration.
// routes/users.js
export default [
{ path: '/users', element: <UsersList /> },
{ path: '/users/:id', element: <UserProfile /> },
];
// routes/admin.js
export default [
{ path: '/admin/dashboard', element: <AdminDashboard /> },
{ path: '/admin/users', element: <AdminUsers /> },
];
// App.js
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import usersRoutes from './routes/users';
import adminRoutes from './routes/admin';
function App() {
return (
<Router>
<Routes>
{usersRoutes.map((route, index) => (
<Route key={index} {...route} />
))}
{adminRoutes.map((route, index) => (
<Route key={index} {...route} />
))}
<Route path="*" element={<ErrorPage />} />
</Routes>
</Router>
);
}
15. Route Guards
Create custom guard functions to control route access, often for authorization or data preloading.
function requireAuth(nextState, replace) {
if (!isAuthenticated()) {
replace({
pathname: '/login',
state: { nextPathname: nextState.location.pathname },
});
}
}
// In routes
<Route path="/protected" onEnter={requireAuth} element={<ProtectedComponent />} />
In v6, use useEffect
or useLayoutEffect
for similar functionality.
16. Nested Routing
Nested routes are handled without explicitly wrapping <Switch>
, using nested <Routes>
:
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<MainLayout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="users" element={<Users />}>
<Route path=":userId" element={<User />} />
</Route>
</Route>
<Route path="*" element={<Error404 />} />
</Routes>
</Router>
);
}
Here, /users/:userId
is defined within /users
, rendering both Users
and User
components for /users/someId
.
17. Custom Error Handling
React Router v6 doesn’t provide global error handling, but you can use window.onerror
or custom middleware.
18. Code Splitting and Lazy Loading
React Router v6 integrates with Webpack or other bundlers for code splitting and lazy loading using dynamic import()
:
<Route
path="/lazy"
element={
import('./LazyComponent')
.then((mod) => mod.LazyComponent)
.catch((error) => <ErrorBoundary error={error} />)
}
/>
19. Custom Route Components
While the <Route>
component is simplified, you can create custom components to extend functionality, such as adding loading indicators.
20. Server-Side Rendering (SSR)
React Router v6 supports SSR with additional configuration and libraries like react-router-dom/server
and react-router-dom/browser
.
🚀 Join my technical exchange group to get daily useful information:
Top comments (0)