How to Programmatically Navigate Using React Router Navigate in React Applications
Use the useNavigate hook from react-router-dom to get a navigate function, then call it with a path string or number to programmatically redirect users without using anchor tags.
Programmatic navigation is essential when you need to redirect users after form submissions, authentication checks, or conditional logic flows. The react router navigate API in React Router v6 provides a clean, hook-based approach that replaces the legacy useHistory hook with a more intuitive interface.
Understanding the useNavigate Hook
The useNavigate hook is the primary mechanism for programmatic navigation in React Router v6. Unlike the previous useHistory hook which provided a history object, useNavigate returns a function that you can invoke immediately to trigger navigation.
This approach simplifies the API surface and aligns with React's functional component patterns. The hook must be called within a component rendered by a Router provider, otherwise it will throw an error.
Basic Syntax and Parameters
The navigate function accepts two arguments: a to parameter and an optional options object.
import { useNavigate } from 'react-router-dom';
function MyComponent() {
const navigate = useNavigate();
// Basic navigation
navigate('/dashboard');
// Navigation with options
navigate('/profile', {
state: { from: 'homepage' },
replace: true
});
// Relative navigation
navigate('..');
}
The to parameter can be:
- A string representing the absolute or relative path
- A number representing delta in the history stack (e.g.,
-1for back)
Practical Implementation Examples
Basic Navigation
Trigger navigation after a successful API call or user action:
import { useNavigate } from 'react-router-dom';
function LoginForm() {
const navigate = useNavigate();
const handleSubmit = async (credentials) => {
try {
await api.login(credentials);
navigate('/dashboard'); // Redirect after success
} catch (error) {
console.error('Login failed:', error);
}
};
return <form onSubmit={handleSubmit}>{/* form fields */}</form>;
}
Navigation with State
Pass data to the destination route without exposing it in the URL:
function ProductList() {
const navigate = useNavigate();
const handleProductClick = (product) => {
navigate(`/product/${product.id}`, {
state: {
productName: product.name,
referrer: 'product-list'
}
});
};
return (
<ul>
{products.map(p => (
<li key={p.id} onClick={() => handleProductClick(p)}>
{p.name}
</li>
))}
</ul>
);
}
// Access state in destination component
function ProductDetail() {
const location = useLocation();
const { productName, referrer } = location.state || {};
return <div>Viewing {productName} from {referrer}</div>;
}
Replace vs Push Navigation
Control history stack behavior to prevent users from returning to sensitive intermediate pages:
function CheckoutFlow() {
const navigate = useNavigate();
const processPayment = async () => {
// Process payment...
// Replace current history entry instead of pushing new one
// User won't be able to go back to payment processing page
navigate('/order-confirmation', { replace: true });
};
return <button onClick={processPayment}>Complete Purchase</button>;
}
Relative Navigation
Navigate relative to the current route using dot notation:
function FileBrowser() {
const navigate = useNavigate();
return (
<div>
<button onClick={() => navigate('.')}>Refresh Current</button>
<button onClick={() => navigate('..')}>Go to Parent</button>
<button onClick={() => navigate('./details')}>Go to Child</button>
</div>
);
}
Common Patterns and Best Practices
Handle Navigation in Event Handlers: Always invoke navigate inside user interaction handlers or useEffect hooks, not during render phases to avoid infinite loops.
Type Safety with TypeScript: Define route parameters and state types for compile-time safety:
import { useNavigate } from 'react-router-dom';
interface LocationState {
from: string;
userId: number;
}
function Component() {
const navigate = useNavigate();
const handleClick = () => {
navigate('/dashboard', {
state: { from: 'home', userId: 123 } as LocationState
});
};
}
Conditional Navigation: Combine with authentication or permission checks:
function ProtectedAction() {
const navigate = useNavigate();
const { isAuthenticated } = useAuth();
const handleAction = () => {
if (!isAuthenticated) {
navigate('/login', { state: { from: location.pathname } });
return;
}
// Perform action...
};
return <button onClick={handleAction}>Perform Action</button>;
}
Summary
- Use
useNavigatefromreact-router-domto obtain the navigate function for programmatic routing in React Router v6. - Call with a path string to push a new history entry, or pass
{ replace: true }to replace the current entry instead. - Pass state objects via the options parameter to transfer data between routes without URL pollution.
- Use relative paths (
.or..) to navigate based on the current route location rather than absolute URLs. - Always invoke navigation inside event handlers or effects, never during the render phase, to prevent unexpected behavior.
Frequently Asked Questions
How do I navigate programmatically in React Router v6?
Use the useNavigate hook inside your functional component to get a navigation function. Call this function with your target path as the first argument. For example: const navigate = useNavigate(); navigate('/dashboard');. This replaces the old useHistory hook from v5 and provides a simpler, more direct API for programmatic navigation.
What is the difference between navigate and Link in React Router?
The Link component is used for declarative navigation in your JSX, typically rendered as an anchor tag that users click. The navigate function obtained from useNavigate is used for imperative navigation inside JavaScript logic, such as after form submissions, API responses, or conditional checks. Use Link for standard user navigation and navigate when you need to redirect programmatically based on application logic.
How do I pass data when navigating programmatically in React Router?
Pass a state object as the second argument to the navigate function: navigate('/profile', { state: { userId: 123, from: 'homepage' } }). In the destination component, access this data using the useLocation hook: const location = useLocation(); const { userId } = location.state || {}. This state persists through browser sessions and does not appear in the URL, making it ideal for passing temporary data between routes.
How do I prevent users from going back to the previous page after navigation?
Include the replace: true option when calling navigate: navigate('/dashboard', { replace: true }). This replaces the current history entry instead of pushing a new one onto the stack. Users clicking the browser back button will skip the replaced page and go to the page before it. This pattern is commonly used after login forms, payment processing, or intermediate loading states that shouldn't be revisited via back navigation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s https://instagit.com/install.md