Files
4395d7c807 Fix repeated navigations while the Instant Navigation lock is held (#95864)
With the Instant Navigation Testing lock held (the Navigation Inspector
paused), repeated client navigations between pages that share a layout
could sometimes get stuck without ever resolving.

The root cause turned out to be a subtle problem with the client
router's use of `useDeferredValue` to switch between the cached UI and
the final UI. The mechanism only works as we intend when mounting a new
part of the tree. When updating an existing part of the tree, like a
shared layout, React will skip the cached UI entirely.

We were already aware of this issue with the `useDeferredValue`
approach. The plan is to instead model cached navigation as an
optimistic update followed by a transition update. Before we can make
such a change, though, we need to make a change in React to allow
optimistic updates to suspend (currently, optimistic updates act like
sync updates, and can trigger existing Suspense boundaries to switch
back to their fallback state).

To workaround this issue when using the Navigation Inspector, we're
going to change how the inspector works when multiple navigations are
performed during the same "lock". Instead of keeping the entire sequence
blocked until the end, we will instead only block the most recent one.
This more closely matches what the inspector UI implies, anyway: it
doesn't show a stack of pending navigations, only the most recent one.

This doesn't absolve us from needing to switch to using an optimistic
update. That's needed regardless to address similar behavior in
production. But this sidesteps the issue for now, and we were planning
to make this change to the Navigation Inspector regardless.

Co-authored-by: Sam Selikoff <[email protected]>
2026-07-16 20:19:57 -04:00

89 lines
2.6 KiB
TypeScript

import Link from 'next/link'
import { ErrorTrigger } from './error-trigger'
export default function Page() {
return (
<div>
<h1 data-testid="home-title">Instant Navigation Mode Demo</h1>
<ErrorTrigger />
<p>
This fixture tests the <strong>Instant Navigation Mode</strong> toggle
in Next.js Dev Tools. When enabled, navigations show only the cached or
prefetched state — dynamic data is not streamed.
</p>
<h2>How to test</h2>
<ol>
<li>
Open <strong>Next.js Dev Tools</strong> (click the Next.js logo in the
corner).
</li>
<li>
Toggle <strong>Instant Navigation Mode</strong> to <em>On</em>. The
indicator turns blue.
</li>
<li>
Click the link below. You should see the loading skeleton instead of
the final page content.
</li>
<li>
Click the blue <em>Instant UI only</em> indicator to unblock dynamic
data and resume normal navigation.
</li>
</ol>
<nav style={{ marginTop: '1.5rem' }}>
<div>
<Link
href="/target-page/my-post?search=foo"
id="link-to-target"
style={{
display: 'inline-block',
padding: '0.5rem 1rem',
background: '#0070f3',
color: '#fff',
borderRadius: 6,
textDecoration: 'none',
}}
>
Go to target page &rarr;
</Link>
<Link
href="/target-page/my-post?search=foo"
id="link-to-target-prefetch"
prefetch={true}
style={{
display: 'inline-block',
marginLeft: '0.75rem',
padding: '0.5rem 1rem',
background: '#7c3aed',
color: '#fff',
borderRadius: 6,
textDecoration: 'none',
}}
>
Go to target page (prefetch) &rarr;
</Link>
<Link
href="/mpa-target"
id="link-to-mpa-target"
style={{
display: 'inline-block',
marginLeft: '0.75rem',
padding: '0.5rem 1rem',
background: '#111',
color: '#fff',
borderRadius: 6,
textDecoration: 'none',
}}
>
Go to MPA target &rarr;
</Link>
</div>
<div>
<Link href="/await-connection">Page with await connection</Link>
</div>
</nav>
</div>
)
}