Instant view switches with Inertia v3 prefetching original

by Freek Van der Herten – 3 minute read

Over the past few months we've been building There There at Spatie, a support tool shaped by the two decades we've spent running our own customer support. The goal is simple: the helpdesk we always wished we had.

We care about using AI in a particular way. It should help support agents write better replies, not substitute for them. The human stays in charge of the conversation, and the model does the unglamorous work of drafting, rephrasing, and suggesting links. There There is in private beta right now, and you can apply for early access at there-there.app.

We're building There There with Laravel and Inertia, and we lean heavily on the latest features Inertia v3 brings. This post is about another one: prefetching on hover.

Using prefetch on hover

In a helpdesk, a support agent flips between views constantly. My open tickets, unassigned, waiting, spam, team views, custom filters. Every switch triggers a fresh page load against a different filter. If each switch blocks on a round-trip, the sidebar starts to feel like dead weight.

Inertia v3 has a built-in prefetching API that covers this neatly. Instead of waiting for the click, we fetch the view's data as soon as the cursor hovers over the link, and keep the result in a short-lived cache so the actual navigation feels instant.

Here's the core of our sidebar view link.

<Link
    href={buildViewUrl(view)}
    prefetch="hover"
    cacheFor="30s"
    preserveState={isOnTickets}
    preserveScroll={isOnTickets}
>
    <Icon className="size-4" />
    <span>{view.label}</span>
</Link>

prefetch="hover" is the whole mechanism. After the cursor sits on the link for 75ms, Inertia fires the underlying request in the background. When the agent actually clicks, the response is already in memory, and the page swaps without a network wait.

cacheFor is where the real tuning happens. The default is 30 seconds. For our ticket views, that's a sweet spot: long enough that flipping back and forth feels free, short enough that the counts don't go visibly stale. You can push it higher for more static lists, or drop it to "0" for views where freshness matters more than speed.

Here it is in action. The cursor hovers, Inertia warms the cache in the background, and the click lands on data that's already there.

One small gotcha is worth flagging. When an agent navigates away from a view, you often want to cancel any in-flight requests so you don't paint stale data. The obvious call is router.cancelAll(), but that also cancels prefetches that are happily warming the cache for views the agent is about to visit.

Inertia v3 lets you scope that. Here's how we cancel the active visit without touching prefetches.

router.cancelAll({ async: false, prefetch: false });

The flags tell Inertia to keep background prefetches and async visits (like an infinite scroll) alive, while cancelling the normal page visit. The agent gets an immediate navigation and the sidebar stays primed for the next move.

In closing

Prefetching on hover is one of those features that costs almost nothing to turn on and changes how the app feels. One prop, a cache duration, and you're done. The cancellation scoping is the one detail worth knowing about, because without it you'll undo your own optimisation the first time you navigate away from a view.

You can read more in the Inertia v3 prefetching guide. And if you'd like to try There There yourself, we're in private beta right now and you can apply for early access at there-there.app.

Join 9,500+ smart developers

Get my monthly newsletter with what I learn from running Spatie, building Oh Dear, and maintaining 300+ open source packages. Practical takes on Laravel, PHP, and AI that you can actually use.

No spam. Unsubscribe anytime. You can also follow me on X.

Found something interesting to share? Submit a link to the community section.