How to Fix: Link Component does not prefetch on network recovery.
Encountering an issue where Next.js’s <Link> component fails to prefetch routes after a network interruption can significantly degrade user experience. This specific bug manifests when a user’s network connection recovers, and the expected client-side prefetching of linked pages does not re-engage, leading to slower navigations than anticipated.
Table of Contents
Understanding the Root Cause
The Next.js <Link> component is designed for optimized client-side navigation. A core feature is prefetching, where Next.js automatically downloads the JavaScript and data for a linked page in the background, typically on mouseover or touchstart events. This ensures near-instantaneous page transitions.
The root of this particular bug lies in how the Next.js router and the <Link> component handle internal state changes related to network status. When a network connection drops, the browser’s navigator.onLine property changes, and ongoing network requests might fail. Upon network recovery (navigator.onLine becoming true again), the <Link> component’s internal logic, or the router’s prefetching mechanism, may not properly detect this change to re-queue or re-initiate prefetch requests.
Specifically:
- Stale State: The
<Link>component or the underlying client-side router might maintain a “stale” network state, failing to recognize that the network has recovered. - Event Listener Discrepancies: The prefetching logic, often triggered by mouse or touch events, might not re-register or re-evaluate the network status effectively after an interruption, leading to missed opportunities to prefetch.
- Hydration and Remounts: The issue description mentions “unnecessary mounts.” This could indicate that component re-renders or unmount/mount cycles, potentially triggered by network changes, are not correctly resetting the prefetch state or are happening in a way that interferes with the default prefetch mechanism. This is particularly relevant in Next.js’s hydration process, where client-side React takes over from server-rendered HTML.
- Internal Next.js Router Logic: Given that this is a specific bug reported on GitHub, it likely points to an edge case in the Next.js framework’s internal handling of network events and router state synchronization, especially in newer or canary versions. The router might simply not have a robust mechanism to re-evaluate all potential prefetch targets after an
onlineevent.
This results in users experiencing a full page load delay instead of the expected instant navigation, as the linked page’s assets are only fetched when the user actually clicks the link, not preemptively.
Step-by-Step Solution
While awaiting a potential fix in future Next.js releases (especially if this is a framework-level bug in canary versions), we can implement a robust client-side workaround to ensure prefetching resumes efficiently after network recovery.
Step 1: Create a Custom Network Status Hook
We’ll start by creating a React hook to monitor the browser’s online/offline status. This hook will provide us with a reactive way to detect network changes.
// hooks/useNetworkStatus.ts
import { useState, useEffect } from 'react';
export function useNetworkStatus() {
const [isOnline, setIsOnline] = useState(typeof navigator !== 'undefined' ? navigator.onLine : true);
useEffect(() => {
const handleOnline = () => setIsOnline(true);
const handleOffline = () => setIsOnline(false);
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
// Initial check in case the state was missed during mount
setIsOnline(navigator.onLine);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
}
Step 2: Implement a Manual Prefetcher Component (Pages Router Specific)
Next, we’ll create a component that leverages this hook. When the network comes back online, it will iterate through a defined set of critical paths and manually trigger router.prefetch(). This ensures that key pages are preloaded even if the <Link> component’s default mechanism falters. This solution is primarily for the Pages Router due to its use of router.prefetch.
// components/NetworkRecoveryPrefetcher.tsx
import { useEffect, useCallback } from 'react';
import { useRouter } from 'next/router'; // IMPORTANT: This `useRouter` is for Pages Router
import { useNetworkStatus } from '../hooks/useNetworkStatus';
// Define the critical paths you want to proactively prefetch
const CRITICAL_PATHS_TO_PREFETCH = [
'/',
'/about',
'/products',
// Add any other routes that are crucial for immediate access
];
export function NetworkRecoveryPrefetcher() {
const isOnline = useNetworkStatus();
const router = useRouter();
const triggerPrefetch = useCallback(() => {
console.log('Network recovered. Triggering manual prefetch for critical paths.');
CRITICAL_PATHS_TO_PREFETCH.forEach(path => {
// Ensure the router is ready before attempting to prefetch
if (router.isReady) {
router.prefetch(path).catch(err => {
console.error(`Failed to prefetch ${path}:`, err);
});
}
});
}, [router]);
useEffect(() => {
// Only trigger prefetch when `isOnline` becomes true
if (isOnline) {
// Small delay to allow browser to fully re-establish connection if needed
const timeoutId = setTimeout(triggerPrefetch, 500);
return () => clearTimeout(timeoutId);
}
}, [isOnline, triggerPrefetch]);
// This component doesn't render anything visible
return null;
}
Step 3: Integrate into Your Application Layout
Finally, place the NetworkRecoveryPrefetcher component in your root layout or _app.tsx so it’s active across your entire application.
For Pages Router (_app.tsx):
// pages/_app.tsx
import type { AppProps } from 'next/app';
import { NetworkRecoveryPrefetcher } from '../components/NetworkRecoveryPrefetcher';
function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<Component {...pageProps} />
<NetworkRecoveryPrefetcher />
</>
);
}
export default MyApp;
Considerations for App Router:
If you are using the Next.js App Router, the useRouter hook from next/navigation does not expose a prefetch method. next/link in the App Router handles prefetching automatically and is generally more robust in its internal logic.
If the <Link> component itself is failing to prefetch after network recovery in an App Router context, the issue is more likely a fundamental bug within the Next.js framework’s core <Link> component (as hinted by the GitHub issue linking to link.tsx). In such a scenario, the most direct and recommended solution is to:
- Ensure your Next.js version is up-to-date, as framework-level bugs like this are typically patched in newer releases.
- Verify that your
<Link>components do not have theprefetch={false}prop explicitly set, which would disable prefetching. - Confirm that no client-side JavaScript is interfering with the
<Link>component’s default behavior, especially during hydration or after network events.
The manual prefetching workaround presented above is specifically tailored for scenarios where programmatic prefetching (like router.prefetch) is available and desired, which is common in the Pages Router architecture.
Common Edge Cases
- Over-prefetching: Manually prefetching too many routes, especially large ones, immediately after network recovery can lead to a burst of network requests, potentially saturating the user’s connection or increasing server load. Be judicious with
CRITICAL_PATHS_TO_PREFETCH. - Browser Compatibility: While
navigator.onLineis widely supported, subtle differences in how browsers report network status (e.g., distinguishing between no connection and a throttled connection) can exist. - Race Conditions: If the network recovers extremely quickly, or if other parts of your application also react to network status, there could be slight race conditions. The small
setTimeoutdelay in theNetworkRecoveryPrefetcherhelps mitigate this. - Router Readiness: Ensure that
router.isReady(for Pages Router) or the equivalent setup in App Router is checked before attempting any router operations, especially during initial hydration or when components mount. - Dynamic Routes: The current
CRITICAL_PATHS_TO_PREFETCHarray is static. For dynamic routes (e.g.,/products/[id]), you would need more sophisticated logic to determine which specific dynamic paths to prefetch, perhaps based on recent user activity or commonly accessed items. - Client-side Only: The
useNetworkStatushook and the prefetching logic are strictly client-side. This solution does not impact server-side rendering (SSR) or static site generation (SSG) processes, but rather enhances the client-side experience.
FAQ
- Q: Why is prefetching important for user experience?
- A: Prefetching allows the browser to download necessary resources (JavaScript, CSS, data) for upcoming pages in the background, before the user actually navigates to them. This makes subsequent page transitions feel instantaneous, significantly improving the perception of speed and overall user experience, especially on slower networks.
- Q: Does this bug affect all Next.js versions?
- A: While the issue description points to
canaryversions, suggesting it might be a recent regression or an active area of development, similar network recovery issues can sometimes appear in stable versions as well, though less frequently. It’s always best practice to keep your Next.js dependencies updated to benefit from the latest bug fixes and performance improvements. - Q: How can I verify if prefetching is working correctly after applying this fix?
- A: You can verify prefetching using your browser’s developer tools. Go to the “Network” tab, set your network to “Offline”, then bring it “Online”. Observe the network requests. You should see requests for the critical paths defined in
CRITICAL_PATHS_TO_PREFETCHshortly after recovery, typically with a “prefetch” initiator or similar. Also, observe subsequent navigations; they should be faster if the prefetching succeeded.