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:pathorindex“ - 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
elementprop:
<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
elementprop with JSX (notcomponentorrender) - 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
elementprop syntax
Always ensure your routes are properly wrapped in <Routes> and your entire app is wrapped in a router component like <BrowserRouter>.
