Proper Route Configuration in React Router v6
A common mistake when migrating to React Router v6 is not wrapping <Route>
components inside a <Routes>
component. This change was introduced in v6 to provide several improvements over the previous routing system.
The Problem: Unwrapped Routes
// ❌ Incorrect in React Router v6
function App() {
return (
<BrowserRouter>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</BrowserRouter>
);
}
Why This Doesn’t Work in v6
- New Architecture:
<Routes>
is now required as the routing container - Performance Benefits: Enables better route matching algorithm
- Relative Routing: Provides context for nested routes
- Error Handling: Better error detection for misconfigured routes
Correct Implementation
1. Basic Routing
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>
);
}
2. With Layout Components
function App() {
return (
<BrowserRouter>
<Routes>
<Route element={<MainLayout />}>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Route>
<Route path="/login" element={<Login />} />
</Routes>
</BrowserRouter>
);
}
3. Nested Routes
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/users" element={<UsersLayout />}>
<Route index element={<UsersList />} />
<Route path=":id" element={<UserProfile />} />
</Route>
</Routes>
</BrowserRouter>
);
}
Key Differences from v5
Feature | v5 | v6 |
---|---|---|
Container | <Switch> | <Routes> |
Route Matching | First match | Best match |
Relative Paths | More complex | Simplified |
Nesting | props.children | <Outlet> component |
Default Props | component , render | element prop only |
Common Migration Issues
- Missing
<Routes>
Wrapper - Using
component
Prop (nowelement
)
// v5
<Route path="/" component={Home} />
// v6
<Route path="/" element={<Home />} />
- Not Using
<Outlet>
for Nested Routes - Forgetting
index
for Index Routes
Best Practices
- Always Wrap Routes: Every
<Route>
must be inside<Routes>
- Use JSX Elements: Pass components as
<Component />
not justComponent
- Leverage Nested Routing: Use the new nested route structure
- Type Safety: In TypeScript, type your route paths
- Error Boundaries: Wrap routes in error boundaries for better UX
Advanced Patterns
1. Route Configuration Object
const routes = [
{ path: '/', element: <Home /> },
{ path: '/about', element: <About /> },
];
function App() {
return (
<BrowserRouter>
<Routes>
{routes.map((route) => (
<Route key={route.path} {...route} />
))}
</Routes>
</BrowserRouter>
);
}
2. Authentication Flow
function RequireAuth({ children }) {
const auth = useAuth();
const location = useLocation();
if (!auth.user) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
return children;
}
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/login" element={<Login />} />
<Route
path="/protected"
element={
<RequireAuth>
<ProtectedPage />
</RequireAuth>
}
/>
</Routes>
</BrowserRouter>
);
}
Remember: The <Routes>
component in v6 provides significant improvements in route matching and nested routing capabilities. Always wrap your routes in <Routes>
and take advantage of the new features for better performance and maintainability.