![]()
React Router v6 introduced a significant change in how routes must be structured, requiring all <Route> components to be wrapped in a <Routes> component. This change often catches developers off guard when migrating from v5 or learning the new version.
The Core Problem
In React Router v6, this configuration won’t work:
import { BrowserRouter, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Route path="/" element={<Home />} /> {/* ❌ Not wrapped in Routes */}
<Route path="/about" element={<About />} />
</BrowserRouter>
);
}
The Correct Approach
All routes must be wrapped in a <Routes> component:
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes> {/* ✅ Mandatory wrapper */}
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>
);
}
Key Differences from v5
| Feature | React Router v5 | React Router v6 |
|---|---|---|
| Route Wrapper | Optional <Switch> | Required <Routes> |
| Route Matching | First-match wins | Best-match algorithm |
| Element Prop | component or render | Only element |
| Nesting | Flat structure | Hierarchical structure |
Common Mistakes and Solutions
❌ Using component or render props
<Route path="/old" component={OldComponent} /> {/* ❌ v5 syntax */}
✅ Fix: Use element prop
<Route path="/new" element={<NewComponent />} /> {/* ✅ v6 syntax */}
❌ Forgetting to wrap multiple routes
<BrowserRouter>
<Route path="/one" element={<One />} />
<Route path="/two" element={<Two />} />
</BrowserRouter>
✅ Fix: Wrap with <Routes>
<BrowserRouter>
<Routes>
<Route path="/one" element={<One />} />
<Route path="/two" element={<Two />} />
</Routes>
</BrowserRouter>
❌ Mixing v5 and v6 patterns
<BrowserRouter>
<Switch> {/* ❌ v5 Switch */}
<Route path="/" element={<Home />} /> {/* ❌ v6 element prop */}
</Switch>
</BrowserRouter>
✅ Fix: Commit to v6 pattern
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
</Routes>
</BrowserRouter>
Advanced Patterns
1. Nested Routes
<Routes>
<Route path="/user" element={<UserLayout />}>
<Route index element={<UserDashboard />} />
<Route path="profile" element={<UserProfile />} />
<Route path="settings" element={<UserSettings />} />
</Route>
</Routes>
// In UserLayout.js
import { Outlet } from 'react-router-dom';
function UserLayout() {
return (
<div>
<UserHeader />
<Outlet /> {/* Nested routes render here */}
<UserFooter />
</div>
);
}
2. Route Configuration Objects
const routes = [
{
path: '/',
element: <Home />,
},
{
path: '/about',
element: <About />,
},
];
function App() {
return (
<BrowserRouter>
<Routes>
{routes.map((route) => (
<Route key={route.path} {...route} />
))}
</Routes>
</BrowserRouter>
);
}
3. Authentication Flow
<Routes>
<Route path="/" element={<PublicLayout />}>
<Route index element={<Home />} />
<Route path="login" element={<Login />} />
</Route>
<Route path="/app" element={<PrivateRoute />}>
<Route index element={<Dashboard />} />
<Route path="profile" element={<Profile />} />
</Route>
</Routes>
// PrivateRoute.js
function PrivateRoute() {
const { user } = useAuth();
if (!user) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
return <Outlet />;
}
Error Messages to Watch For
- “A may have only one child element”
- Solution: Wrap multiple elements in a fragment or layout component
- ” must be inside a “
- Solution: Ensure you have a
<BrowserRouter>or similar at the top level
- “useRoutes() may be used only in the context of a component”
- Solution: Same as above – check your router setup
Migration Tips from v5 to v6
- Replace all
<Switch>with<Routes> - Convert
component={Component}toelement={<Component />} - Move route ranking from the order in
<Switch>to path specificity - Use
relative="path"for relative links instead ofmatch.url - Replace
useHistory()withuseNavigate()
Key Takeaways
- Always wrap your
<Route>components in a<Routes>parent - Use the
elementprop instead ofcomponentorrender - Take advantage of the new nested routes structure
- Remember that route matching is now smarter but stricter
- The v6 API is more consistent and declarative
Proper route configuration in v6 leads to more maintainable and predictable routing in your React applications. While the changes from v5 require some adjustment, the improved API design and additional features make the upgrade worthwhile.
