![]()
Replacing <Redirect> with <Navigate> in React Router v6
React Router v6 removed the <Redirect> component from v5 and replaced it with the more flexible <Navigate> component. This change reflects React Router’s shift toward a hooks-based API and more declarative navigation patterns.
The Problem (Using v5 Pattern in v6)
// ❌ Wrong - Redirect doesn't exist in v6
import { Redirect } from 'react-router-dom';
function OldComponent() {
return <Redirect to="/dashboard" />;
}
Correct Solutions
1. Basic Navigation Replacement
// ✅ Correct - Using Navigate component
import { Navigate } from 'react-router-dom';
function ProtectedRoute({ isAuthenticated }) {
return isAuthenticated
? <Dashboard />
: <Navigate to="/login" replace />;
}
2. With State (Like Redirect’s push)
<Navigate to="/dashboard" state={{ from: location }} replace={false} />
3. Using useNavigate Hook (Programmatic)
import { useNavigate } from 'react-router-dom';
function LoginPage() {
const navigate = useNavigate();
const handleLogin = () => {
login().then(() => {
navigate('/dashboard', { replace: true });
});
};
return <button onClick={handleLogin}>Login</button>;
}
Key Differences
| Feature | v5 <Redirect> | v6 <Navigate> |
|---|---|---|
| Import | react-router-dom | react-router-dom |
| Replacement | push prop | replace prop |
| State | from prop | state prop |
| Rendering | Immediate | Can be conditional |
| Hook version | useHistory().push() | useNavigate() |
Common Migration Scenarios
1. Auth Redirects
// v5
<Route path="/profile">
{isLoggedIn ? <Profile /> : <Redirect to="/login" />}
</Route>
// v6
<Route
path="/profile"
element={isLoggedIn ? <Profile /> : <Navigate to="/login" />}
/>
2. Route Guards
// v6 Protected Route component
function ProtectedRoute({ children }) {
const { user } = useAuth();
if (!user) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
return children;
}
// Usage
<Route
path="/dashboard"
element={<ProtectedRoute><Dashboard /></ProtectedRoute>}
/>
3. Default/Index Routes
// v5
<Redirect from="/" to="/home" />
// v6
<Route path="/" element={<Navigate to="/home" replace />} />
Best Practices
- Prefer
replacefor auth flows to prevent back navigation - Pass state when redirecting to maintain context
- Use hooks for programmatic navigation
- TypeScript users – Type your location state:
<Navigate
to="/login"
state={{ from: location.pathname } as { from: string }}
/>
- Combine with loaders in data routers:
const router = createBrowserRouter([
{
path: '/old',
loader: () => redirect('/new')
}
]);
Advanced Patterns
1. Delayed Redirect
function MessageWithRedirect() {
const [shouldRedirect, setShouldRedirect] = useState(false);
useEffect(() => {
const timer = setTimeout(() => setShouldRedirect(true), 3000);
return () => clearTimeout(timer);
}, []);
return shouldRedirect
? <Navigate to="/success" />
: <div>Operation completed! Redirecting...</div>;
}
2. Query Parameter Preservation
function RedirectWithQuery() {
const location = useLocation();
return (
<Navigate
to={`/login?redirect=${encodeURIComponent(location.pathname)}`}
replace
/>
);
}
3. Conditional Redirects
function CheckoutFlow() {
const { step } = useParams();
const orderComplete = useOrderStatus();
if (orderComplete) {
return <Navigate to="/confirmation" />;
}
return (
<div>
{step === 'shipping' && <ShippingStep />}
{step === 'payment' && <PaymentStep />}
</div>
);
}
Remember: <Navigate> is more powerful than the old <Redirect> because:
- It can be used anywhere in your component tree
- It works with React’s component lifecycle
- It integrates with React Router’s data APIs
- It supports all navigation features (state, replace, etc.)
For most use cases, you’ll want to:
- Use
<Navigate>for component-based declarative navigation - Use
useNavigate()hook for imperative navigation - Use
redirect()function in loaders (data routers)
