NeuroAgent

Programmatic Navigation in React Router: Complete Guide

Learn how to implement programmatic navigation in React Router v6 using useNavigate hook. Navigate from dropdowns, forms, and other components without Link elements. Complete guide with code examples.

Question

How can I programmatically navigate using React Router?

With React Router, I can use the Link element to create links which are natively handled by React Router. I see internally it calls this.context.transitionTo(...).

I want to do navigation. Not from a link, but from a dropdown selection (as an example). How can I do this in code? What is this.context?

I saw the Navigation mixin, but can I do this without mixins?

NeuroAgent

Programmatic navigation in React Router v6 is achieved using the useNavigate hook for functional components or the useHistory hook in older versions. You can call the returned function with the destination path or location object to trigger navigation from any component, including dropdown selections or other interactive elements.

Contents

Getting Started with Programmatic Navigation

Programmatic navigation in React Router allows you to control navigation from within your application’s logic rather than relying solely on declarative <Link> components. This is particularly useful for scenarios where navigation should be triggered based on user interactions like form submissions, dropdown selections, or after asynchronous operations complete.

In modern React Router v6, the primary approach involves using the useNavigate hook, which provides a function that can be called with either a string path or a location object to trigger navigation. This approach replaces the older withRouter higher-order component and the Navigation mixin mentioned in your question.

The this.context you mentioned refers to React’s context API, which was previously used by React Router to provide routing information to components. However, in modern React applications, hooks have become the preferred approach for accessing context and other React features.

Using the useNavigate Hook

The useNavigate hook is the standard way to handle programmatic navigation in React Router v6. Here’s how to implement it:

jsx
import { useNavigate } from 'react-router-dom';

function MyComponent() {
  const navigate = useNavigate();
  
  const handleDropdownChange = (event) => {
    const selectedValue = event.target.value;
    navigate(selectedValue);
  };
  
  return (
    <select onChange={handleDropdownChange}>
      <option value="/home">Home</option>
      <option value="/about">About</option>
      <option value="/contact">Contact</option>
    </select>
  );
}

Navigation with Options

You can also pass options to the navigate function for more control:

jsx
const navigate = useNavigate();

// Navigate with replace option (doesn't add to history)
navigate('/dashboard', { replace: true });

// Navigate with state
navigate('/profile', { 
  state: { from: '/dashboard' },
  replace: false
});

Relative Navigation

For relative navigation, you can use the relative option:

jsx
// Navigate one level up
navigate('..', { relative: 'path' });

// Navigate within the same route but with different params
navigate('..', { relative: 'path' });

Navigation from Class Components

While functional components with hooks are the modern approach, you may still need to handle navigation in class components. Here are several methods:

Using React Router Context

You can access the router context directly in class components:

jsx
import { useLocation, useNavigate } from 'react-router-dom';

class MyComponent extends React.Component {
  static contextType = RouterContext;
  
  handleNavigation = () => {
    this.context.navigate('/new-path');
  };
  
  render() {
    return <button onClick={this.handleNavigation}>Navigate</button>;
  }
}

Higher-Order Component Pattern

Create a higher-order component to inject navigation:

jsx
import { useNavigate } from 'react-router-dom';

function withRouter(Component) {
  function ComponentWithRouterProp(props) {
    let navigate = useNavigate();
    return <Component {...props} navigate={navigate} />;
  }
  return ComponentWithRouterProp;
}

// Usage in class component
class MyComponent extends React.Component {
  handleClick = () => {
    this.props.navigate('/target');
  };
  
  render() {
    return <button onClick={this.handleClick}>Navigate</button>;
  }
}

export default withRouter(MyComponent);

Advanced Navigation Options

Navigation with Location Objects

Instead of simple strings, you can pass complete location objects:

jsx
const location = {
  pathname: '/users',
  search: '?sort=asc',
  hash: '#section1',
  state: { from: '/dashboard' }
};

navigate(location);

Programmatic Navigation with Confirmation

You can add confirmation dialogs before navigation:

jsx
const handleNavigate = () => {
  if (window.confirm('Are you sure you want to leave?')) {
    navigate('/new-page');
  }
};

Navigation Based on Conditions

jsx
const navigate = useNavigate();

const handleSubmit = async () => {
  const data = await submitForm();
  if (data.success) {
    navigate('/success');
  } else {
    navigate('/error');
  }
};

When navigating programmatically, you often want to pass state to the destination component:

jsx
// Source component
const navigate = useNavigate();
const handleClick = () => {
  navigate('/profile', {
    state: { 
      userId: '123',
      timestamp: Date.now(),
      referrer: '/dashboard'
    }
  });
};

// Destination component
import { useLocation } from 'react-router-dom';

function ProfilePage() {
  const location = useLocation();
  const { state } = location;
  
  // Access passed state
  const userId = state?.userId;
  const referrer = state?.referrer;
  
  return <div>Profile for user {userId}</div>;
}

Navigation State Persistence

Note that navigation state is not persisted across browser refreshes. For data that needs to persist, consider using URL parameters or a state management solution.

Migration from v5 to v6

If you’re migrating from React Router v5, here are the key changes:

v5 to v6 Migration Guide

v5 History Hook:

jsx
// React Router v5
import { useHistory } from 'react-router-dom';

function Component() {
  const history = useHistory();
  history.push('/new-path');
}

v6 Navigate Hook:

jsx
// React Router v6
import { useNavigate } from 'react-router-dom';

function Component() {
  const navigate = useNavigate();
  navigate('/new-path');
}

Key Differences

  1. Hook Names: useHistoryuseNavigate
  2. Method Names: history.push()navigate()
  3. Options: history.push(path, state)navigate(path, { state })
  4. Relative Navigation: New relative option in v6

Common Migration Patterns

Replacing withRouter:

jsx
// v5 - withRouter HOC
export default withRouter(MyComponent);

// v6 - Custom HOC
import { useNavigate } from 'react-router-dom';

function withNavigation(Component) {
  return function WrappedComponent(props) {
    const navigate = useNavigate();
    return <Component {...props} navigate={navigate} />;
  };
}

Best Practices

1. Prefer Hooks Over Context

Modern React Router encourages using hooks over the older context approach:

jsx
// Recommended: Using hooks
const navigate = useNavigate();

// Not recommended: Direct context access
const context = useContext(RouterContext);

2. Handle Navigation Loading States

Provide feedback during navigation:

jsx
function SubmitForm() {
  const [isSubmitting, setIsSubmitting] = useState(false);
  const navigate = useNavigate();
  
  const handleSubmit = async () => {
    setIsSubmitting(true);
    try {
      await submitData();
      navigate('/success');
    } catch (error) {
      navigate('/error');
    } finally {
      setIsSubmitting(false);
    }
  };
  
  return (
    <button 
      onClick={handleSubmit} 
      disabled={isSubmitting}
    >
      {isSubmitting ? 'Submitting...' : 'Submit'}
    </button>
  );
}

3. Use TypeScript for Type Safety

When using TypeScript, you can get better type safety:

typescript
import { useNavigate, NavigateFunction } from 'react-router-dom';

function Component() {
  const navigate: NavigateFunction = useNavigate();
  
  // TypeScript will help catch invalid paths
  navigate('/valid-path'); // ✅
  navigate('/invalid-path'); // ❌ Will show type error
}

4. Avoid Hardcoded Paths

Use constants or configuration for navigation paths:

jsx
const ROUTES = {
  HOME: '/',
  ABOUT: '/about',
  DASHBOARD: '/dashboard',
  PROFILE: '/profile',
};

function Component() {
  const navigate = useNavigate();
  
  const navigateToDashboard = () => {
    navigate(ROUTES.DASHBOARD);
  };
}

5. Handle Navigation Errors

Implement error handling for navigation:

jsx
import { useNavigate } from 'react-router-dom';
import { toast } from 'react-toastify';

function Component() {
  const navigate = useNavigate();
  
  const safeNavigate = (path: string) => {
    try {
      navigate(path);
    } catch (error) {
      toast.error('Navigation failed');
      console.error('Navigation error:', error);
    }
  };
}

Conclusion

Programmatic navigation in React Router v6 is straightforward using the useNavigate hook, which provides a clean, modern approach to controlling navigation from within your application logic. The hook returns a function that can be called with either string paths or location objects, giving you full control over how and when navigation occurs.

Key takeaways include:

  • Use useNavigate() for programmatic navigation in functional components
  • Pass location objects for complex navigation scenarios with parameters and state
  • Create higher-order components for class components that need access to navigation
  • Handle navigation state carefully, remembering it’s not persisted across refreshes
  • Follow modern React patterns and use TypeScript for better type safety

The older approaches using context directly or mixins like the Navigation mixin are no longer recommended in favor of the hook-based approach, which aligns better with modern React development practices and provides better performance and type safety.

Sources

  1. React Router Documentation - useNavigate Hook
  2. React Router v6 Migration Guide
  3. React Router API Reference
  4. React Router v6 Programmatic Navigation Tutorial