useNavigation

Despite the near-identical name, useNavigation() has nothing to do with useNavigate(). useNavigate() is an imperative function you call to change the URL yourself – see that page for details. useNavigation(), covered here, is the opposite kind of thing: a read-only object describing a navigation that's already happening elsewhere in the app (a link click, a form submission, a redirect from a loader), so you can render pending UI for it. Neither hook can do the other's job.

useNavigation() returns a navigation object whose "state" is one of "idle" (nothing in progress), "loading" (a route's loader is running for the next location), or "submitting" (a route's action is running because of a form submission). While state is anything other than "idle", "location" holds the destination the app is navigating to, and, for a submission, "formData", "formMethod", and "formAction" describe what's being submitted.


import React from "react";
import { createBrowserRouter, Link, Outlet, useNavigation } from "react-router";
import { RouterProvider } from "react-router/dom";

function Layout() {
  let navigation = useNavigation();

  return (
    <div>
      <ul>
        <li><Link to="/">Home</Link></li>
        <li><Link to="/about">About</Link></li>
      </ul>
      {navigation.state !== "idle" && <p>Loading...</p>}
      <Outlet />
    </div>
  );
}

const router = createBrowserRouter([
  {
    path: "/",
    element: <Layout />,
    children: [
      { index: true, element: <h2>Home</h2> },
      {
        path: "about",
        element: <h2>About</h2>,
        loader: () =>
          new Promise((resolve) => {
            setTimeout(() => resolve(null), 800);
          }),
      },
    ],
  },
]);

export default function UseNavigationExample() {
  return <RouterProvider router={router} />;
}

Checking navigation.state !== "idle" like this in a top-level layout is a common way to drive a single global loading indicator, no matter which route it's for. For a way to re-run the current route's loaders on demand, without waiting for a navigation, see useRevalidator.