React Router v6 introduced significant changes to its navigation API, replacing the history object with the useNavigate() hook. This change often causes confusion for developers migrating from v5 or trying to use old patterns in v6.
Key Differences Between v5 and v6
| Feature | React Router v5 | React Router v6 |
|---|---|---|
| Navigation method | history.push() | useNavigate() |
| Access method | useHistory() hook | useNavigate() hook |
| Relative paths | Limited support | Full support |
| API style | Object-based (history) | Function-based (navigate) |
The Problem: Using history.push() in v6
In React Router v6, this code won’t work:
import { useHistory } from 'react-router-dom';
function MyComponent() {
const history = useHistory(); // ❌ Not available in v6
const handleClick = () => {
history.push('/new-route'); // ❌ Old pattern
};
return <button onClick={handleClick}>Go</button>;
}
The Solution: Using useNavigate()
Here’s how to properly navigate in v6:
import { useNavigate } from 'react-router-dom';
function MyComponent() {
const navigate = useNavigate(); // ✅ Correct v6 approach
const handleClick = () => {
navigate('/new-route'); // ✅ Uses the new API
};
return <button onClick={handleClick}>Go</button>;
}
Common Migration Scenarios
1. Basic Navigation
v5:
history.push('/dashboard');
v6:
navigate('/dashboard');
2. Navigation with State
v5:
history.push('/user', { userData: 123 });
v6:
navigate('/user', { state: { userData: 123 } });
3. Replace Instead of Push
v5:
history.replace('/login');
v6:
navigate('/login', { replace: true });
4. Relative Navigation
v5:
history.push(`${match.url}/details`);
v6 (better relative navigation):
navigate('details'); // Relative to current route
Advanced Patterns
1. Programmatic Navigation Outside Components
For navigation in Redux actions or services:
// Create a history instance (only do this once in your app)
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();
// In your app setup
import { unstable_HistoryRouter as HistoryRouter } from 'react-router-dom';
import { history } from './history';
<HistoryRouter history={history}>
<App />
</HistoryRouter>
// Then use anywhere in your app
history.push('/somewhere');
2. Using Navigate Component
For conditional redirects in JSX:
import { Navigate } from 'react-router-dom';
function ProtectedRoute({ user }) {
if (!user) {
return <Navigate to="/login" replace />;
}
return <Dashboard />;
}
3. Custom Navigation Hook
Create a reusable hook:
import { useNavigate } from 'react-router-dom';
export function useNavigation() {
const navigate = useNavigate();
const goTo = (path, options = {}) => {
navigate(path, options);
};
return { goTo };
}
// Usage
const { goTo } = useNavigation();
goTo('/products');
Handling Common Errors
Error: “useNavigate() may be used only in the context of a component”
Solution: Wrap your app with a router:
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
Error: “history.push is not a function”
Solution: You’re mixing v5 and v6 patterns. Stick to useNavigate() in v6.
Backward Compatibility
If you need to maintain both v5 and v6 during migration:
// navigation-utils.js
import { useNavigate } from 'react-router-dom';
export function useLegacyNavigation() {
const navigate = useNavigate();
return {
push: (path, state) => navigate(path, { state }),
replace: (path) => navigate(path, { replace: true }),
// Add other history methods as needed
};
}
// Usage
const history = useLegacyNavigation();
history.push('/old-path');
Key Takeaways
- React Router v6 replaces
history.push()withuseNavigate() - The new API is more consistent and supports better relative navigation
- For programmatic navigation outside components, create a custom history instance
- Use the
Navigatecomponent for declarative redirects - Migration requires updating all
historyusage tonavigate
The new navigation API in v6 provides a more intuitive and flexible way to handle routing in your React applications. While it requires some changes from v5 patterns, the improvements in relative navigation and API consistency make it worth the effort to upgrade.
