Using history.push() in React Router v6 instead of useNavigate()

Loading

Using history.push() in React Router v6

In React Router v6, the traditional history.push() approach from v4/v5 has been replaced with the useNavigate() hook. Attempting to use the old history API directly will cause errors.

The Problem

// ❌ Wrong - Old v5 approach in v6
import { createBrowserHistory } from 'history';
const history = createBrowserHistory();

function App() {
  const handleClick = () => {
    history.push('/dashboard'); // Doesn't work in v6
  };

  return <button onClick={handleClick}>Go to Dashboard</button>;
}

Why this fails in v6:

  1. React Router v6 removed direct history access
  2. Navigation is now handled through the useNavigate hook
  3. The history object isn’t automatically synchronized with the router
  4. Results in broken navigation state

Correct Solutions

1. Basic useNavigate Usage (Recommended)

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

function App() {
  const navigate = useNavigate();

  const handleClick = () => {
    navigate('/dashboard'); // ✅ Correct v6 approach
  };

  return <button onClick={handleClick}>Go to Dashboard</button>;
}

2. Replace Navigation (Like replaceState)

function App() {
  const navigate = useNavigate();

  const handleClick = () => {
    navigate('/dashboard', { replace: true }); // ✅ Replaces current entry
  };

  return <button onClick={handleClick}>Login</button>;
}

3. With State

navigate('/user', {
  state: { id: 123 }, // ✅ Pass state
  replace: false // Default
});

4. Relative Navigation

navigate('../parent-route'); // ✅ Relative paths
navigate(-1); // ✅ Go back (like history.goBack())
navigate(2); // ✅ Go forward 2 entries

Migration from v5 to v6

Old v5 Pattern

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

function OldComponent() {
  const history = useHistory();

  history.push('/path');
  history.replace('/path');
  history.goBack();
}

New v6 Pattern

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

function NewComponent() {
  const navigate = useNavigate();

  navigate('/path'); // push
  navigate('/path', { replace: true }); // replace
  navigate(-1); // goBack
}

Advanced Patterns

1. Custom History Wrapper (For Special Cases)

import { createBrowserHistory } from 'history';
import { unstable_HistoryRouter as HistoryRouter } from 'react-router-dom';

const history = createBrowserHistory();

// In your app setup
<HistoryRouter history={history}>
  <App />
</HistoryRouter>

// Now can use both approaches
history.push('/old-way');
navigate('/new-way'); // Also works

2. Programmatic Navigation Outside Components

// navigation.js
let navigateRef;

export const setNavigate = (navigate) => {
  navigateRef = navigate;
};

export const navigate = (to) => {
  if (navigateRef) navigateRef(to);
};

// App.jsx
import { useNavigate } from 'react-router-dom';
import { setNavigate } from './navigation';

function App() {
  const navigate = useNavigate();
  setNavigate(navigate);
  // ...
}

// Anywhere else
import { navigate } from './navigation';
navigate('/somewhere');

3. TypeScript Support

const navigate = useNavigate();

// Typed state
navigate('/user', {
  state: { userId: 123 } // ✅ Type-safe state
});

// Receiving state
const { state } = useLocation();
const userId = state?.userId; // Type: number | undefined

Common Mistakes

  1. Using v5 code in v6:
   import { useHistory } from 'react-router-dom'; // ❌ v5 only
  1. Forgetting the navigate function:
   // ❌ Missing hook call
   <button onClick={() => useNavigate()('/path')}>Click</button>
  1. Incorrect relative paths:
   navigate('./path'); // ❌ Might not work as expected

Best Practices

  1. Use useNavigate for all navigation in v6
  2. Keep navigation centralized in custom hooks/services
  3. Use the options object for replace/state
  4. Prefer absolute paths for main routes
  5. Test navigation with MemoryRouter in tests

Leave a Reply

Your email address will not be published. Required fields are marked *