Proper Usage of useNavigate()
in React Router
A common error in React Router v6+ occurs when trying to use the useNavigate()
hook outside of a <Router>
context. This hook requires access to the routing context provided by React Router’s router components.
The Error Scenario
// ❌ Wrong - using useNavigate outside Router context
function StandaloneButton() {
const navigate = useNavigate(); // Error!
return <button onClick={() => navigate('/')}>Home</button>;
}
function App() {
return (
<div>
<StandaloneButton />
</div>
);
}
Correct Solutions
1. Wrap Your App with a Router
// ✅ Correct - entire app inside Router
import { BrowserRouter as Router } from 'react-router-dom';
function App() {
return (
<Router>
{/* All routing-dependent components */}
<StandaloneButton />
</Router>
);
}
function StandaloneButton() {
const navigate = useNavigate(); // Works now
return <button onClick={() => navigate('/')}>Home</button>;
}
2. Alternative: Pass navigate as a Prop
// ✅ Alternative pattern
function App() {
return (
<Router>
<Layout>
<Routes>
<Route path="/" element={<Home />} />
</Routes>
</Layout>
</Router>
);
}
function Layout({ children }) {
const navigate = useNavigate();
return (
<div>
<NavBar navigate={navigate} />
{children}
</div>
);
}
function NavBar({ navigate }) {
// Receives navigate as prop from parent
return <button onClick={() => navigate('/')}>Home</button>;
}
Common Use Cases
1. Programmatic Navigation
function LoginForm() {
const navigate = useNavigate();
const handleSubmit = async () => {
await loginUser();
navigate('/dashboard'); // Redirect after login
};
return <form onSubmit={handleSubmit}>...</form>;
}
2. Navigation in Custom Hooks
function useAuth() {
const navigate = useNavigate();
const login = async () => {
await authenticate();
navigate('/profile');
};
return { login };
}
Troubleshooting
If you’re still seeing errors:
- Verify you’re using React Router v6+ (earlier versions used
useHistory
) - Check your router import matches your routing style:
import { BrowserRouter, HashRouter, MemoryRouter } from 'react-router-dom';
- Ensure no component using
useNavigate
renders before the<Router>
- For testing, you may need to wrap components in a
MemoryRouter
Testing Components with useNavigate
// In your test file
import { render } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
test('navigates on click', () => {
render(
<MemoryRouter>
<ComponentUsingNavigate />
</MemoryRouter>
);
// Test implementation
});
Remember that useNavigate()
is a hook that requires the React Router context to be available in the component hierarchy. Always ensure your navigation-dependent components are rendered within a router component.