Not Wrapping <Route>
Inside <Routes>
in React Router v6
A common mistake when migrating to React Router v6 is forgetting to wrap route definitions in a <Routes>
component (or its ancestor <BrowserRouter>
). This breaks the routing functionality because v6 introduced a new routing architecture.
The Problem
// ❌ Wrong - Routes not wrapped
import { Route } from 'react-router-dom';
function App() {
return (
<div>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</div>
);
}
Why this fails:
- React Router v6 requires
<Routes>
as a route container - Without it, route matching and prioritization won’t work
- Results in error: “A
<Route>
may have only one of the following props:path
orindex
“ - Routes won’t be properly matched or rendered
Correct Solutions
1. Basic Routes Wrapping (Recommended)
import { Routes, Route } from 'react-router-dom';
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
);
}
2. With BrowserRouter (Full Setup)
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="*" element={<NotFound />} /> {/* Catch-all route */}
</Routes>
</BrowserRouter>
);
}
3. Nested Routes
function App() {
return (
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="users" element={<Users />}>
<Route path=":id" element={<UserProfile />} />
</Route>
</Route>
</Routes>
);
}
Key Differences from v5
Feature | v5 | v6 |
---|---|---|
Container | <Switch> | <Routes> |
Route matching | First match | Best match |
Element prop | component or render | element |
Nesting | Explicit in route path | Hierarchical <Route> |
Relative paths | Manual handling | Automatic resolution |
Common Mistakes
- Using v5 syntax in v6:
<Switch> {/* ❌ v5 only */}
<Route exact path="/" component={Home} />
</Switch>
- Forgetting the
element
prop:
<Route path="/about"><About /></Route> {/* ❌ Wrong in v6 */}
- Mixing v5 and v6 packages:
// ❌ Incompatible versions
import { Routes } from 'react-router';
import { Route } from 'react-router-dom';
- Missing parent routes:
<Routes>
<Route path="users/:id" element={<UserProfile />} /> {/* ❌ Missing /users */}
</Routes>
Best Practices
- Always wrap routes in
<Routes>
- Use
element
prop with JSX (notcomponent
orrender
) - Leverage nested routes for layout structures
- Order routes from specific to general:
<Route path="users/:id" element={<UserDetail />} />
<Route path="users" element={<UserList />} />
- Use index routes for default child routes
Advanced Patterns
1. Route Configuration Objects
const routes = [
{ path: '/', element: <Home /> },
{ path: '/about', element: <About /> },
];
function App() {
return (
<Routes>
{routes.map((route) => (
<Route key={route.path} {...route} />
))}
</Routes>
);
}
2. Protected Routes
function PrivateRoute({ children }) {
const auth = useAuth();
return auth ? children : <Navigate to="/login" />;
}
<Routes>
<Route path="/" element={<PublicPage />} />
<Route
path="/dashboard"
element={
<PrivateRoute>
<Dashboard />
</PrivateRoute>
}
/>
</Routes>
3. Error Boundaries
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
Remember: React Router v6’s <Routes>
component is smarter than v5’s <Switch>
. It:
- Automatically picks the best matching route
- Enables relative routing and links
- Supports nested routes more elegantly
- Requires the
element
prop syntax
Always ensure your routes are properly wrapped in <Routes>
and your entire app is wrapped in a router component like <BrowserRouter>
.