useLinkStatus: Track Link Pending State
Table of Contents
- Introduction — Why Link Status Matters
- What is useLinkStatus?
- Syntax and Usage
- Usage Examples: Simple to Advanced
- More useLinkStatus Examples
- Conclusion
basicutils.com
Introduction — Why useLinkStatus Matters
In modern single-page apps, a user might click on a link and nothing seems to happen. This is mainly due to two reasons:
- Prefetching is disabled or still in progress.
- The target route is dynamic and needs to load data or components before rendering.
In either case, the user is having a poor experience on your website. useLinkStatus was created to solve this specific problem. useLinkStatus is a hook that lets you monitor the pending state of a link. In response, you can show a spinner, shimmer text, or any other relevant visual feedback.
What is useLinkStatus?
useLinkStatus is a React hook provided by Next.js to track the pending state of a link. The hook returns an object with a single property:
- pending: A boolean value that indicates if the link is in a pending state (i.e., the navigation is still in progress). It will be true before the route transition is complete and false afterward.
When to use useLinkStatus hook
useLinkStatus is useful in the following situations:
- Prefetching is disabled or still in progress: This ensures that there is some delay, allowing your pending state logic to execute and be visible to the user.
- The destination route is dynamic and doesn't include a loading.js file: This again ensures there is a delay for the pending state logic. The loading.js file shouldn't be available because it would show up instead of the pending state logic.
Syntax and Usage of useLinkStatus
Basic Syntax of useLinkStatus
Here is how to call the hook.
const { pending } = useLinkStatus()
- pending: A boolean value that indicates whether the link is in a pending state.
- useLinkStatus must be used within a descendant component of a Link component.
- If you were to click multiple links quickly, only the last links pending UI is shown
- useLinkStatus doesn't work in pages router.
- The hook is most useful when prefetch={false} is set to the Link component.
Code Example: Creating a Basic Spinner using useLinkStatus
The below code shows how to create a loading indicator using the useLinkStatus hook.
'use client'
import { useLinkStatus } from 'next/link'
export default function LoadingComponent() {
const { pending } = useLinkStatus()
return pending ? <div className="spinner" aria-busy="true" /> : null
}
Using the Spinner Inside a Link Component
The below code shows how to use the above-created spinner inside a Next.js Link component.
import { useLinkStatus } from 'next/link'
import Link from 'next/link'
import LoadingComponent from './components/loading-component'
export default function Header() {
return (
<header>
<Link href="/dashboard" prefetch={false}>
Dashboard <LoadingComponent />
</Link>
</header>
)
}
Explanation of Code
- We use useLinkStatus inside the loading component.
- We use the loading component inside the Next.js link to show the loading state when the link is clicked.
- We have set prefetch to false so that the code works.
Usage Examples of useLinkStatus
🔹 Minimal Spinner Example
'use client'
import { useLinkStatus } from 'next/link'
export default function LoadingIndicator() {
const { pending } = useLinkStatus()
return pending ? <div className="spinner" aria-busy="true" /> : null
}
🔹 MUI Button with Inline Feedback
import { useLinkStatus } from 'next/link'
import Link from 'next/link'
import { Button, CircularProgress } from '@mui/material'
export function NavItem() {
const { pending } = useLinkStatus()
return (
<Link href="/dashboard" prefetch={false}>
<Button endIcon={pending && <CircularProgress size={16} />} aria-busy={pending}>
Dashboard
</Button>
</Link>
)
}
🔹 Debounced Indicator (for fast nav handling)
const [showSpinner, setShowSpinner] = useState(false)
useEffect(() => {
let timeout: NodeJS.Timeout
if (pending) {
timeout = setTimeout(() => setShowSpinner(true), 100)
} else {
setShowSpinner(false)
clearTimeout(timeout)
}
return () => clearTimeout(timeout)
}, [pending])
More useLinkStatus Examples
import { useLinkStatus } from 'next/link'
import Link from 'next/link'
import { Button, CircularProgress } from '@mui/material'
export default function LinkButton({ href, children }: { href: string, children: React.ReactNode }) {
const { pending } = useLinkStatus()
return (
<Link href={href} prefetch={false}>
<a>
<Button
variant="contained"
disabled={pending}
endIcon={pending ? <CircularProgress size={16} /> : null}
>
{children}
</Button>
</a>
</Link>
)
}
Explanation
Above, we use a Material UI Button component.
The button shows a CircularProgress when the link is in a pending state.
The button is disabled when the navigation is in progress to prevent multiple clicks.
Conclusion
useLinkStatus is a small but very powerful hook that can improve the user experience on your websites. By giving you real-time insight into when a link is pending, it allows you to give good visual feedback.
In modern apps, even a small delay can create a negative image of the website for the user.
Frequently Asked Questions
Does useLinkStatus work with all Link components?
No. useLinkStatus only works inside components that are rendered as descendants of a Link component from next/link. It does not work globally or outside of a link’s subtree.
Why is the pending state always false?
There are a few common reasons: - You're using the Pages Router, where this hook is not supported. - The link has already been prefetched, which means the navigation happens instantly, skipping the pending state. - The hook is not placed within the Link’s descendant tree, so it’s not wired to any link context.
Can I use useLinkStatus on multiple links at the same time?
Yes, but note that only the most recently clicked link will report pending: true. If multiple links are clicked rapidly, the pending state will shift to the last one clicked.
How do I avoid showing the spinner for fast navigations?
You can add a short delay before showing the spinner (e.g., 100–200ms). This avoids unnecessary flashes on fast route transitions. Combine useEffect and setTimeout with pending for best results.
Does useLinkStatus require prefetch={false}?
Not strictly, but it’s most useful when prefetch={false} is set. If prefetching is enabled and completes early, the page will load instantly and skip the pending state.
Can I customize what happens when a link is pending?
Yes. useLinkStatus gives you full control. You can show: - A spinner or loader - Shimmer text or skeletons - Disable buttons or links - Apply CSS transitions or animations
Is it available in earlier versions of Next.js?
No. useLinkStatus was introduced in Next.js 15.3.0. It’s not available in earlier versions.
References
Background References
About the Author
Joseph Horace
Horace is a dedicated software developer with a deep passion for technology and problem-solving. With years of experience in developing robust and scalable applications, Horace specializes in building user-friendly solutions using cutting-edge technologies. His expertise spans across multiple areas of software development, with a focus on delivering high-quality code and seamless user experiences. Horace believes in continuous learning and enjoys sharing insights with the community through contributions and collaborations. When not coding, he enjoys exploring new technologies and staying updated on industry trends.