Skip to content

Navigating Back in React Router v6

Problem

In previous versions of React Router, developers could navigate back to the previous route using the history.goBack() method. With the introduction of React Router v6, the API changed significantly, leaving many developers unsure how to implement back navigation correctly.

Solution

React Router v6 introduces the useNavigate hook, which provides navigation capabilities including the ability to go back in the history stack.

Basic Back Navigation

The simplest way to navigate back is using a negative delta value:

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

function MyComponent() {
  const navigate = useNavigate();
  
  return (
    <button onClick={() => navigate(-1)}>
      Go Back
    </button>
  );
}

You can navigate multiple pages back or forward by providing different delta values:

jsx
function NavigationControls() {
  const navigate = useNavigate();
  
  return (
    <div>
      <button onClick={() => navigate(-2)}>Go 2 pages back</button>
      <button onClick={() => navigate(-1)}>Go back</button>
      <button onClick={() => navigate(1)}>Go forward</button>
      <button onClick={() => navigate(2)}>Go 2 pages forward</button>
    </div>
  );
}

Advanced Navigation Patterns

Handling Empty History Stack

In some cases (like when a link opens in a new tab), navigating back might not be possible. Here's how to handle this scenario:

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

function BackButton() {
  const navigate = useNavigate();
  
  const handleGoBack = () => {
    if (window.history?.length && window.history.length > 1) {
      navigate(-1);
    } else {
      navigate('/', { replace: true });
    }
  };
  
  return (
    <button onClick={handleGoBack}>
      Go Back
    </button>
  );
}
jsx
import { useNavigate, useLocation } from 'react-router-dom';

function BackLink() {
  const navigate = useNavigate();
  const location = useLocation();
  
  // Check if we can navigate back
  const canGoBack = location.key !== 'default';
  const href = canGoBack ? -1 : '/';
  
  return (
    <button onClick={() => navigate(href)}>
      Back to Safety
    </button>
  );
}

Explicit Redirect State Management

For authentication flows or other scenarios where you need precise control over redirects:

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

function LoginPage() {
  const location = useLocation();
  const navigate = useNavigate();
  
  const handleLogin = () => {
    // After successful login
    const from = location.state?.from || '/dashboard';
    navigate(from, { replace: true });
  };
  
  return (
    // Login form that calls handleLogin on success
  );
}

// When redirecting to login
function ProtectedComponent() {
  const navigate = useNavigate();
  const location = useLocation();
  
  const redirectToLogin = () => {
    navigate('/login', { 
      state: { from: location.pathname } 
    });
  };
  
  // Redirect logic
}

You can also use the Link component with relative paths:

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

function BackLink() {
  return (
    <Link to=".." relative="path">
      Go Back
    </Link>
  );
}

TypeScript Note

The Link component in React Router v6 doesn't officially support numbers in its to prop according to TypeScript definitions, though passing -1 may work at runtime.

Best Practices

  1. Always handle the empty history case - Don't assume there's always a page to go back to
  2. Use state for explicit redirects - When authentication is involved, explicitly store the return location
  3. Consider user experience - Provide appropriate fallbacks when back navigation isn't possible
  4. Test across scenarios - Ensure your navigation works when links open in new tabs/windows

Common Pitfalls

DANGER

Avoid unconditionally calling navigate(-1) as it may leave users stranded if there's no history to go back to.

The solutions provided ensure robust navigation that works across different scenarios while maintaining a positive user experience. Choose the approach that best fits your specific use case, keeping in mind the need to handle edge cases where backward navigation might not be available.