A common mistake in React Router v6+ is trying to use the useNavigate()
hook outside the context of a <BrowserRouter>
(or similar router component). This error occurs because navigation hooks require router context to function properly.
The Error You’ll See
import { useNavigate } from 'react-router-dom';
function App() {
const navigate = useNavigate(); // ❌ Error!
// Error: useNavigate() may be used only in the context of a <Router> component.
return <div>My App</div>;
}
Why This Happens
React Router’s hooks rely on React context to provide routing functionality. The <BrowserRouter>
(or other routers) creates this context, so any components using routing hooks must be descendants of a router component.
Correct Implementation
1. Basic Setup with BrowserRouter
import { BrowserRouter, Routes, Route, useNavigate } from 'react-router-dom';
function App() {
return (
<BrowserRouter> {/* ✅ Provides router context */}
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/about" element={<AboutPage />} />
</Routes>
</BrowserRouter>
);
}
function HomePage() {
const navigate = useNavigate(); // ✅ Works inside router
return (
<div>
<h1>Home</h1>
<button onClick={() => navigate('/about')}>Go to About</button>
</div>
);
}
2. Using in Nested Components
function ProductPage() {
const navigate = useNavigate(); // ✅ Works in any child component
const handleBuy = () => {
// Perform purchase logic
navigate('/confirmation');
};
return (
<div>
<ProductDetails />
<button onClick={handleBuy}>Buy Now</button>
</div>
);
}
Common Mistakes and Solutions
❌ Using in Root Component Without Router
function App() {
const navigate = useNavigate(); // ❌ Won't work
return (
<div>
<BrowserRouter>
{/* ... */}
</BrowserRouter>
</div>
);
}
✅ Fix: Move router to top level
function Root() {
return (
<BrowserRouter>
<App /> {/* Now App can use routing hooks */}
</BrowserRouter>
);
}
function App() {
const navigate = useNavigate(); // ✅ Now works
// ...
}
❌ Using in Utility Functions Outside Components
// api.js - ❌ Won't work
export function handleLogout() {
const navigate = useNavigate(); // Error
// ...
}
✅ Fix: Pass navigate as parameter
// api.js
export function handleLogout(navigate) {
// Clear auth state
navigate('/login');
}
// Component.js
function UserProfile() {
const navigate = useNavigate();
const logout = () => {
handleLogout(navigate); // ✅ Pass the navigate function
};
// ...
}
Alternative Solutions
1. Using withRouter (Class Components)
For class components (where hooks can’t be used):
import { withRouter } from 'react-router-dom';
class MyComponent extends React.Component {
handleClick = () => {
this.props.history.push('/new-route'); // Uses history API
};
render() {
return <button onClick={this.handleClick}>Navigate</button>;
}
}
export default withRouter(MyComponent);
2. Using Navigate Component
For conditional redirects in render:
import { Navigate } from 'react-router-dom';
function ProtectedRoute({ user }) {
if (!user) {
return <Navigate to="/login" replace />;
}
return <Dashboard />;
}
Testing Considerations
When testing components that use useNavigate()
, you’ll need to wrap them in a router:
test('navigates on button click', () => {
render(
<MemoryRouter>
<MyComponent />
</MemoryRouter>
);
// Test navigation behavior
});
Key Takeaways
- Always wrap your app or component tree with
<BrowserRouter>
(or similar) - useNavigate() only works in components rendered within a router
- For utility functions, pass the navigate function as an argument
- In class components, use
withRouter
HOC or pass navigate as prop - For testing, wrap components in
<MemoryRouter>
Proper usage of useNavigate()
ensures your application’s routing works as expected while maintaining clean component architecture.