Link pending state with useLinkStatus, child component pattern, comparison with useTransition.
Navigation isn't always instant. Slow networks, dynamic routes that can't be prefetched, or simply large payloads can create delay. useLinkStatus provides pending state specifically for <Link> navigations—no transition management required.
The hook returns { pending } and must be used inside a descendant component of the <Link>.
Create a child component that shows loading feedback:
'use client';
import Link, { useLinkStatus } from 'next/link';
function NavIndicator({ children }) {
const { pending } = useLinkStatus();
return (
<>
{pending ? <Spinner className="size-4" /> : null}
{children}
</>
);
}
export function NavLink({ href, children }) {
return (
<Link href={href}>
<NavIndicator>{children}</NavIndicator>
</Link>
);
}Click the link → spinner appears → navigation completes → new page renders.
The spinner appears when navigation takes time:
searchParams or other dynamic data can't be fully prefetchedOn fast connections with fully prefetched routes, the spinner won't appear—and that's ideal.
Like useFormStatus, useLinkStatus must be called from a child of the Link:
// ❌ Won't work - same component as Link
function BadLink() {
const { pending } = useLinkStatus(); // Always false
return <Link href="/page">...</Link
| Approach | Use When |
|---|---|
useLinkStatus | Simple navigation feedback, declarative, works with <Link> |
useTransition + router.push | Need optimistic updates, imperative control, error handling |
Choose useLinkStatus for straightforward navigation feedback. Use useTransition when you need to update state optimistically or handle the navigation outcome programmatically.