DEV Community

Cover image for Instant Navigation: Hover Prefetching in React ⚡
Prajapati Paresh
Prajapati Paresh

Posted on • Originally published at smarttechdevs.in

Instant Navigation: Hover Prefetching in React ⚡

The Post-Click Loading Penalty

In modern React Single Page Applications at Smart Tech Devs, standard routing creates a frustrating UX loop. A user clicks the "View Analytics" link. The URL changes, the Analytics component mounts, a useEffect hook fires to fetch the data, and the user is forced to stare at a spinning loader for 800 milliseconds while the API resolves.

Waiting for a user to explicitly click a link before requesting the data is highly inefficient. Human reaction time is slow. When a user decides to navigate, they move their mouse over the link, pause for a moment, and then click. That mouse-hover pause usually lasts between 200ms and 400ms. If you harness that idle time, you can achieve Instant Navigation with zero loading spinners.

The Solution: Intent-Driven Prefetching

Using TanStack React Query, we can preemptively trigger the API fetch the exact millisecond the user's mouse enters the link bounding box (onMouseEnter).

By the time their brain registers the decision to physically click the mouse button 300ms later, the API request has already completed in the background, and the data is sitting securely in the local React Query cache. When the new page mounts, it renders instantly with zero perceived latency.

Architecting the Prefetch Link Component

Instead of manually wiring up event listeners on every link, we build a reusable, smart <PrefetchLink> wrapper component that handles the caching logic globally.


// components/navigation/PrefetchLink.tsx
"use client";

import Link from 'next/link';
import { useQueryClient } from '@tanstack/react-query';
import { fetchAnalyticsData } from '@/lib/api'; // Your fetcher function

interface PrefetchLinkProps {
    href: string;
    children: React.ReactNode;
    queryKey: string[];
    fetchFn: () => Promise<any>;
}

export default function PrefetchLink({ href, children, queryKey, fetchFn }: PrefetchLinkProps) {
    const queryClient = useQueryClient();

    const handleHover = () => {
        // ✅ THE ENTERPRISE PATTERN: Pre-populate the cache on hover!
        // If the data is already in the cache, React Query is smart enough to do nothing.
        // If it isn't, it fetches it silently in the background before the click occurs.
        queryClient.prefetchQuery({
            queryKey: queryKey,
            queryFn: fetchFn,
            staleTime: 60000, // Keep it fresh for 1 minute so they can hover without spamming the API
        });
    };

    return (
        <Link 
            href={href} 
            onMouseEnter={handleHover}
            onTouchStart={handleHover} // Support mobile tap-intent!
            className="text-purple-600 hover:text-purple-800 font-medium transition-colors"
        >
            {children}
        </Link>
    );
}

Consuming the Pre-Fetched Data

When the user actually clicks the link and navigates to the /analytics page, the component utilizes the standard useQuery hook. Because the queryKey matches the one we pre-fetched, it bypasses the loading state entirely and returns the cached data instantly.


// app/analytics/page.tsx
"use client";

import { useQuery } from '@tanstack/react-query';
import { fetchAnalyticsData } from '@/lib/api';

export default function AnalyticsPage() {
    // Because we hovered the link on the previous page, `isLoading` will be false instantly!
    const { data, isLoading } = useQuery({
        queryKey: ['analytics_dashboard'],
        queryFn: fetchAnalyticsData
    });

    if (isLoading) return <div>Loading...</div>; // The user will almost never see this!

    return (
        <main className="p-8">
            <h1 className="text-2xl font-bold mb-4">Analytics Dashboard</h1>
            {/* Render data... */}
        </main>
    );
}

The Engineering ROI

By implementing Intent-Driven Prefetching, you mask network latency entirely. You transform a heavily web-bound, spinner-filled dashboard into an application that feels natively installed on the user's hard drive, drastically elevating the perceived speed and premium feel of your SaaS product.

Top comments (0)