← Back

useLinkStatus for Navigation

Track Link pending state, SortButton pattern, prefetch={false} for visible feedback.

PublishedMarch 5, 2026257 words
Edit

useLinkStatus for Link Pending State

The useLinkStatus hook provides pending state for <Link> navigations. It must be used inside a descendant component of Link.

When to Use

Use useLinkStatus when:

  • Prefetching is disabled or in progress
  • The destination route is dynamic and doesn't have a loading.js
  • You want inline feedback (spinner, shimmer) on the clicked link itself

The Pattern

'use client'; import Link, { useLinkStatus } from 'next/link'; import { Loader2, ArrowUpDown } from 'lucide-react'; function SortIndicator({ icon: Icon, label }: { icon: typeof ArrowUpDown; label: string }) { const { pending } = useLinkStatus(); return ( <> {pending ? <Loader2 className="size-4 animate-spin" /> : <Icon className="size-4" />} <span>{label}</span> </> ); } export function SortButton() { return ( <Link href="/dashboard?sort=newest" prefetch={false} className="..."> <SortIndicator icon={ArrowUpDown} label="Newest" /> </Link> ); }
'use client'; import Link, { useLinkStatus } from 'next/link'; import { Loader2, ArrowUpDown } from 'lucide-react'; function SortIndicator({ icon:

Key Points

  1. Must be a Link descendant - useLinkStatus only works inside a component rendered within <Link>
  2. Use prefetch={false} - If the route is prefetched, pending state is skipped
  3. Extract to child component - The hook tracks the parent Link's navigation state
  4. Style as Link - Use buttonVariants from shadcn/ui to style Links as buttons

vs useTransition + router.push

| Approach | Pros | Cons | |----------|------|------| | useLinkStatus | Simpler, no state management, declarative | Must use <Link>, no optimistic updates | | useTransition + router.push | Full control, optimistic updates possible | More boilerplate, imperative |

Choose useLinkStatus for simple navigation feedback. Use useTransition when you need optimistic state updates during navigation.

Icon
,
label
}
:
{
icon
:
typeof
ArrowUpDown
;
label
:
string
}
)
{
const
{
pending
}
=
useLinkStatus
(
)
;
return
(
<
>
{
pending
?
<
Loader2
className
=
"
size-4 animate-spin
"
/>
:
<
Icon
className
=
"
size-4
"
/>
}
<
span
>
{
label
}
</
span
>
</
>
)
;
}
export
function
SortButton
(
)
{
return
(
<
Link
href
=
"
/dashboard?sort=newest
"
prefetch
=
{
false
}
className
=
"
...
"
>
<
SortIndicator
icon
=
{
ArrowUpDown
}
label
=
"
Newest
"
/>
</
Link
>
)
;
}