How to intercept browser back button / trackpad swipe in React and redirect to a specific route instead of history back

# Intercepting Browser Back Navigation in React: Pitfalls and Solutions

## Summary
Attempts to forcibly redirect users to a fixed route (e.g., `/settings`) when pressing the back button or using trackpad swipes break expected browser navigation behavior. Using `popstate` to override history entries instead of leveraging React Router's capabilities causes navigation conflicts.

## Root Cause
- The `popstate` handler triggers *after* the history entry updates. Overriding it via `navigate("/settings", { replace: true })` corrupts the history stack.
- Browser navigation controls operate on the history stack. Forcible route replacement interrupts the native linear navigation flow.
- Client-side routing libraries like React Router manage history internally. Manual `popstate` handlers conflict with their state management.

## Why This Happens in Real Systems
- Product requirements demanding "special" navigation flows override default behavior
- UX attempts to guide users from specific exit points
- Inadequate testing of edge cases involving browser gestures/hardware buttons

## Real-World Impact
- Users experience unexpected navigation loops
- Session state mismatches when history entries don't match application state
- Broken swipe gestures on mobile/hybrid devices
- Accessibility violations (loss of predictable navigation)

## Example or Code
Problematic implementation:
```jsx
// ❌ Anti-pattern: Conflict with React Router state
useEffect(() => {
  const handlePopState = () => {
    navigate("/settings", { replace: true });
  };
  window.addEventListener("popstate", handlePopState);
  return () => window.removeEventListener("popstate", handlePopstate);
}, [navigate]);

Alternative pattern using React Router primitives:

// ✅ Recommended solution
import { useBlocker } from 'react-router-dom';

function Page() {
  useBlocker((tx) => {
    tx.retry(); // Cancel navigation transition
    navigate('/settings');
  }, when); // Conditionally activate blocker
}

How Senior Engineers Fix It

  1. Decouple redirection from history manipulation: Track navigation intent via state machines instead of route hijacking
  2. Use routing control points:
    • Implement prompt modals (react-router‘s useBlocker) before overriding navigation
    • Conditionally redirect during route transitions using routers’ lifecycle hooks
  3. Preserve navigation stack integrity:
    • Replace only the next history entry via replace: true strategically
    • Avoid manipulating history during popstate events
  4. UX-centric design:
    • Reserve redirection only for exceptional flows (e.g., checkout exit)
    • Restrict override to specific routes via conditional guards

Why Juniors Miss It

  • Expecting browser navigation to behave like in-app routing primitives
  • Underestimating complexity of browser’s history API synchronization
  • Focusing solely on function implementation (“can override navigation”) over user navigation ergonomics
  • Testing only with back button clicks without verifying swipe/touchpad gestures