Deep-Link Video in Mega Menu: URL, State, Preload Done Right
Three URL layers for mega menu video deep-links: path for identity, query for panel state, hash for timestamp. Manual sync of video.currentTime to hash. Preload metadata + poster, not auto. Prefetch route on hover, not auto.
TL;DR
Use path segments for video identity, query strings for panel state, and hash fragments for timestamps to create bookmarkable URLs. React Activity preserves component state but not video playback, so manually sync currentTime with the hash on mount and timeupdate. For mega panels, set preload="metadata" with posters and manually prefetch on hover since hidden elements skip automatic prefetching.
I clicked a video in the mega panel, the URL changed, but opening it in a new tab — the wrong video loaded, or the playback position reset to zero.
My first guess: just add ?videoId=v-123 to the mega menu URL. Turns out Vercel Academy is clear: resource identity (which video) belongs in the path segment (/video/[videoId]), not the query string. Query string is for optional state — filters, panel open, or timestamp.
Research shows three layers that must be correct.
URL Structure
Path segment for identity (/video/v-123). Query string for panel state (?panel=open). Hash fragment #t=45 for playback position — the Media Fragments URI standard [5]. This combination makes URLs bookmarkable, shareable, and cacheable.
Video State Restoration
React <Activity> preserves component internal state when hidden [4], but not the <video> element state (currentTime, paused). Manual sync is needed: on timeupdate, update window.location.hash to #t=...; on mount, read the hash and set video.currentTime = .... Don't rely on React state for playback position memory.
Preloading
Mega panel has many videos. preload="auto" is recommended by web.dev [3] only for a single hero video. For a panel: preload="metadata" + poster — just fetch duration and dimensions, save bandwidth. Browsers may ignore this hint; Chrome 64+ defaults to metadata.
Prefetch Strategy
Next.js <Link> prefetches automatically on viewport entry [2]. Elements inside hidden <Activity> (display: none) never enter the viewport — so prefetch doesn't fire. Solution: prefetch={false} on <Link>, then router.prefetch() manually on video card hover.
// Generic pattern: video card in mega panel
<Link href={`/video/${video.id}`} prefetch={false}
onMouseEnter={() => router.prefetch(`/video/${video.id}`)}>
<video preload="metadata" poster={video.poster} />
</Link>The video detail page reads params.videoId from the path, searchParams.panel for panel open state, and window.location.hash (#t=...) to seek to the saved position.
Three layers — path for identity, query for state, hash for timestamp — make deep-linked video in mega menus reliable, fast, and bookmark-friendly.
Sources
- Vercel Academy — Params vs SearchParams (2026-10-28)
- Next.js Docs — Linking and Navigating (2026-10-28)
- web.dev — Fast playback with preload (2026-10-28)
- React.dev — Activity reference (2026-10-28)
- MDN — Media Fragments URI (2026-10-28)