Files
next.js/packages/next/src/client/components/navigation-untracked.ts
T
Sebastian "Sebbie" SilbermannandGitHub d3169de7e5 Collect modules with browser variants statically (#95200)
Instead of hardcoding the list, we now automatically detect any
`.browser` sibling and create a compiler alias for that module.

Together with a linting rule, we'll ensure no future regressions are
handled automatically (detection of conditional require and missing
.browser sibling).

This isn't the smoothest DX yet. Ideally we'd generate the list at
Next.js compile time (i.e. before publishing) to avoid having to rerun a
script (I'll follow-up). Generating the list during build time (next dev
or next build) is just another tiny cost users would have to pay.
2026-07-03 08:45:23 +02:00

72 lines
2.6 KiB
TypeScript

import { useContext } from 'react'
import { PathnameContext } from '../../shared/lib/hooks-client-context.shared-runtime'
/**
* This checks to see if the current render has any unknown route parameters that
* would cause the pathname to be dynamic. It's used to trigger a different
* render path in the error boundary.
*
* @returns true if there are any unknown route parameters, false otherwise
*/
function hasFallbackRouteParams(): boolean {
if (typeof window === 'undefined') {
// AsyncLocalStorage should not be included in the client bundle.
const { workUnitAsyncStorage } =
// TODO(browser-variant): migrate to a .ts/.browser.ts split so the browser bundle drops the server branch; see scripts/generate-browser-variant-aliases.mjs
// ast-grep-ignore: no-typeof-window-require
require('../../server/app-render/work-unit-async-storage.external') as typeof import('../../server/app-render/work-unit-async-storage.external')
const workUnitStore = workUnitAsyncStorage.getStore()
if (!workUnitStore) return false
switch (workUnitStore.type) {
case 'prerender':
case 'prerender-client':
case 'prerender-ppr':
case 'validation-client':
const fallbackParams = workUnitStore.fallbackRouteParams
return fallbackParams ? fallbackParams.size > 0 : false
case 'prerender-legacy':
case 'request':
case 'prerender-runtime':
case 'cache':
case 'private-cache':
case 'unstable-cache':
case 'generate-static-params':
break
default:
workUnitStore satisfies never
}
return false
}
return false
}
/**
* This returns a `null` value if there are any unknown route parameters, and
* otherwise returns the pathname from the context. This is an alternative to
* `usePathname` that is used in the error boundary to avoid rendering the
* error boundary when there are unknown route parameters. This doesn't throw
* when accessed with unknown route parameters.
*
* @returns
*
* @internal
*/
export function useUntrackedPathname(): string | null {
// If there are any unknown route parameters we would typically throw
// an error, but this internal method allows us to return a null value instead
// for components that do not propagate the pathname to the static shell (like
// the error boundary).
if (hasFallbackRouteParams()) {
return null
}
// This shouldn't cause any issues related to conditional rendering because
// the environment will be consistent for the render.
// eslint-disable-next-line react-hooks/rules-of-hooks
return useContext(PathnameContext)
}