From 32ea28ec81454b7e902a3d68a55566b0c4e32bb7 Mon Sep 17 00:00:00 2001 From: Marc LeBlanc <7050295+marcleblanc2@users.noreply.github.com> Date: Wed, 16 Sep 2026 18:12:35 -0600 Subject: [PATCH] perf: prefetch sidebar links on hover, not in viewport Next prefetches every that scrolls into view. The sidebar shows dozens at once, so one page load fetched ~80 RSC payloads (377 KB on the wire, 2.4 MB decoded). NavigationLink keeps prefetch off until the user hovers or focuses the link, the pattern Next documents for large lists. --- src/components/Navigation.tsx | 14 +++++++------- src/components/NavigationLink.tsx | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 src/components/NavigationLink.tsx diff --git a/src/components/Navigation.tsx b/src/components/Navigation.tsx index 60a45dd8f..d6b5b9e58 100644 --- a/src/components/Navigation.tsx +++ b/src/components/Navigation.tsx @@ -1,5 +1,5 @@ import clsx from 'clsx'; -import Link from 'next/link'; +import {NavigationLink} from '@/components/NavigationLink'; import {usePathname, useSearchParams} from 'next/navigation'; import {navigation, NavigationItem} from '@/data/navigation'; @@ -147,7 +147,7 @@ export function Navigation({ {separator.topics.map(topic => (
  • - { onLinkClick; @@ -161,7 +161,7 @@ export function Navigation({ )} > {topic.title} - + {topic.sections && (
  • ) )} diff --git a/src/components/NavigationLink.tsx b/src/components/NavigationLink.tsx new file mode 100644 index 000000000..0fb3556d8 --- /dev/null +++ b/src/components/NavigationLink.tsx @@ -0,0 +1,24 @@ +'use client'; + +import Link from 'next/link'; +import {useState} from 'react'; + +type NavigationLinkProps = Omit, 'prefetch'>; + +// The sidebar shows dozens of links at once, and Next's default prefetches +// the full static page for every link in the viewport: one page view fetched +// ~80 RSC payloads (2.4 MB decoded). Prefetch only once the user shows intent. +// https://nextjs.org/docs/app/guides/prefetching#hover-triggered-prefetch +export function NavigationLink(props: NavigationLinkProps) { + const [hasIntent, setHasIntent] = useState(false); + const markIntent = () => setHasIntent(true); + + return ( + + ); +}