Using useNavigate()
Outside of <BrowserRouter>
A common error in React Router v6+ occurs when trying to use the useNavigate()
hook outside of a <BrowserRouter>
context. This hook requires the routing context provided by the router components.
The Error You’ll See
// ❌ This will throw an error:
// "useNavigate() may be used only in the context of a <Router> component"
function StandaloneComponent() {
const navigate = useNavigate();
// ...
}
Why This Happens
- Context Requirement:
useNavigate()
relies on React Router’s context - Component Hierarchy: The hook must be used inside a router component
- Router Types: App must be wrapped in one of:
<BrowserRouter>
<HashRouter>
<MemoryRouter>
<NativeRouter>
<StaticRouter>
Correct Implementation
1. Basic Setup (App Level)
// main.jsx or App.jsx
import { BrowserRouter } from 'react-router-dom';
import App from './App';
ReactDOM.createRoot(document.getElementById('root')).render(
<BrowserRouter>
<App />
</BrowserRouter>
);
2. Using useNavigate Inside Routed Components
function HomePage() {
const navigate = useNavigate(); // ✅ Works inside BrowserRouter
const handleClick = () => {
navigate('/about');
};
return (
<button onClick={handleClick}>
Go to About
</button>
);
}
Solutions for Special Cases
1. Using Navigation Outside Components
For cases where you need navigation outside React components (e.g., in utility functions):
// history.js (singleton)
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();
// Then in your app setup:
import { unstable_HistoryRouter as HistoryRouter } from 'react-router-dom';
import { history } from './history';
ReactDOM.render(
<HistoryRouter history={history}>
<App />
</HistoryRouter>,
document.getElementById('root')
);
// Now you can use it anywhere:
history.push('/some-route');
2. With Redux/State Management
// In your store or action file
import { history } from './history';
export const redirectTo = (path) => () => {
history.push(path);
};
3. Class Components Alternative
For class components, use the navigate
prop from React Router 5 or the wrapper pattern in v6:
// With React Router v6
class MyComponent extends React.Component {
render() {
return (
<NavigateWrapper>
{(navigate) => (
<button onClick={() => navigate('/')}>
Home
</button>
)}
</NavigateWrapper>
);
}
}
function NavigateWrapper({ children }) {
const navigate = useNavigate();
return children(navigate);
}
Common Pitfalls
- Testing Components without router wrapper:
// In tests, wrap component:
test('my test', () => {
render(
<MemoryRouter>
<MyComponent />
</MemoryRouter>
);
});
- Utility Functions trying to use hooks directly
- Third-party Libraries that expect router context
Best Practices
- Centralize routing setup in your app entry point
- Use the history singleton for non-component navigation
- Create custom hooks if you need special navigation logic
- Test with router wrappers
- Consider error boundaries for navigation failures
Remember: useNavigate()
is a hook that requires React Router’s context. Always ensure your component hierarchy includes a router provider at the top level.