useNavigation

useNavigation() has nothing to do with useNavigate() (useNavigate). useNavigate changes the URL; useNavigation is a read-only report on a navigation already under way. It returns one object for the whole router, so a single call in the root layout drives a global progress bar.

navigation.state is "idle", "loading" (loaders running for the next location) or "submitting" (an action running). While it is not idle, navigation.location is the destination, and formData, formMethod, formAction and formEncType describe a submission in flight.

A global bar and a disabled submit buttonJavaScript
function Layout() {
  const { state } = useNavigation();
  return <><div className="bar" hidden={state === 'idle'} /><Outlet /></>;
}
function SaveButton() {
  const nav = useNavigation();
  const saving = nav.state === 'submitting' && nav.formMethod === 'post';
  return <button type="submit" disabled={saving}>{saving ? 'Saving...' : 'Save'}</button>;
}

Two refinements matter in practice. A fast loader makes a spinner flash, so guard the indicator behind a short delay. And because the object is router-wide, a button watching state === 'submitting' also lights up for a submission started elsewhere — check formAction to narrow it, or use a fetcher, which has its own state. navigation.location names the destination before its data arrives — that is how <NavLink> sets isPending.