Migrating from <Redirect>
to <Navigate>
in React Router v6
React Router v6 replaced the <Redirect>
component with <Navigate>
, introducing several improvements in the process. Here’s how to properly handle redirects in v6.
The Problem: Using <Redirect>
in v6
// ❌ Old approach (React Router v5)
import { Redirect } from 'react-router-dom';
function ProtectedRoute() {
const isAuth = useAuth();
return isAuth ? <Dashboard /> : <Redirect to="/login" />;
}
Why This Changed
- Component Renaming: Better reflects its navigation purpose
- Improved Behavior: More consistent with other navigation APIs
- Simplified API: Removes deprecated features
- Type Safety: Better TypeScript support
Correct v6 Implementation
1. Basic Redirect
import { Navigate } from 'react-router-dom';
function ProtectedRoute() {
const isAuth = useAuth();
return isAuth ? <Dashboard /> : <Navigate to="/login" replace />;
}
2. With State
function LoginRedirect() {
return <Navigate to="/login" state={{ from: location }} replace />;
}
// In login component:
const location = useLocation();
const from = location.state?.from || '/';
3. Relative Paths
function TeamRedirect() {
return <Navigate to="../teams" replace />; // Relative to current route
}
Key Differences
Feature | v5 <Redirect> | v6 <Navigate> |
---|---|---|
Import | react-router-dom | react-router-dom |
Props | to , push | to , replace , state |
Default Behavior | push | replace (unless specified) |
Relative Paths | Limited support | Full support |
Common Migration Patterns
1. From Route-Level Redirects
// v5:
<Redirect from="/old-path" to="/new-path" />
// v6:
<Route path="/old-path" element={<Navigate to="/new-path" replace />} />
2. In Route Config (New Data API)
// Using createBrowserRouter (6.4+)
const router = createBrowserRouter([
{
path: '/profile',
loader: async () => {
const user = await getUser();
if (!user) {
throw redirect('/login');
}
return user;
},
element: <Profile />
}
]);
3. Conditional Redirects
function PrivateRoute({ children }) {
const auth = useAuth();
const location = useLocation();
if (!auth.user) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
return children;
}
Best Practices
- Prefer
replace
: Avoids cluttering history (default in v6) - Pass State: Include origin location for better UX
- Use Relative Paths: For more maintainable code
- Type Safety: With TypeScript:
<Navigate to="/login" state={{ from: location.pathname }} />
- Error Boundaries: Wrap redirects that might fail
Common Pitfalls
- Forgetting
replace
:
<Navigate to="/" /> // Adds to history by default
<Navigate to="/" replace /> // Replaces current entry
- Missing State:
// ❌ Loses navigation origin
<Navigate to="/login" />
// ✅ Preserves context
<Navigate to="/login" state={{ from: location }} />
- Infinite Loops:
function Component() {
return <Navigate to="/" />; // ❌ Will continuously redirect
}
Remember: <Navigate>
is the modern replacement for <Redirect>
in React Router v6. It provides more consistent behavior and better integration with React Router’s newer features. Always use it for programmatic navigation in your route components.