MENU
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.