@tanstack/react-router
Version:
Modern and scalable routing for React applications
1 lines • 39 kB
Source Map (JSON)
{"version":3,"file":"link.cjs","names":["React","deepEqual","preloadWarning","getUrlScheme","removeTrailingSlash","useRouter","isServer","matchContext","createLinkScope","functionalUpdate","isDangerousProtocol"],"sources":["../../src/link.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport {\n deepEqual,\n functionalUpdate,\n getUrlScheme,\n isDangerousProtocol,\n preloadWarning,\n removeTrailingSlash,\n} from '@tanstack/router-core'\nimport { isServer } from '@tanstack/router-core/isServer'\nimport { useRouter } from './useRouter'\nimport { createLinkScope, matchContext } from './matchContext'\n\nimport type {\n ActiveOptions,\n AnyRouter,\n Constrain,\n LinkOptions,\n ParsedLocation,\n RegisteredRouter,\n RoutePaths,\n} from '@tanstack/router-core'\nimport type { ReactNode } from 'react'\nimport type {\n ValidateLinkOptions,\n ValidateLinkOptionsArray,\n} from './typePrimitives'\n\n// Undefined active state marks an external or blocked link.\n// Keep that classification with the href instead of parsing it again on render.\ntype LinkState = [href: string | undefined, isActive?: boolean]\n\n// Keep referentially stable values while their contents are equal. Links\n// routinely pass inline `params` / `search` object literals, which would\n// otherwise change `dest` identity on every parent render, rebuild the\n// link state snapshot, and discard its memoized state. One ref holds all of\n// them; each entry is replaced only when its own contents change.\n//\n// The router reuses a built location for as long as it sees the same options\n// object, so the references returned here are its invalidation signal: pass a\n// new object to change a destination. Like every other React prop, an object\n// mutated in place is not re-read. `deepEqual` short-circuits on reference\n// equality, so an unchanged reference costs nothing.\n//\n// `explicitUndefined` is required: an explicit `undefined` clears an\n// inherited param or search key, so `{}` and `{ category: undefined }` build\n// different locations and must not be treated as equal here.\nfunction useStableValues<T extends ReadonlyArray<unknown>>(...values: T): T {\n const ref = React.useRef<ReadonlyArray<unknown>>(values)\n const stable = ref.current as Array<unknown>\n values.forEach((value, index) => {\n if (!deepEqual(stable[index], value, false, true)) {\n stable[index] = value\n }\n })\n return ref.current as T\n}\n\nfunction preloadLink(router: AnyRouter, options: unknown) {\n router.preloadRoute(options as any).catch((err) => {\n console.warn(err)\n console.warn(preloadWarning)\n })\n}\n\nfunction resolveExternalLink(\n to: string | undefined,\n protocolAllowlist: AnyRouter['protocolAllowlist'],\n): string | null | undefined {\n const scheme = typeof to === 'string' && getUrlScheme(to)\n if (!scheme) {\n return undefined\n }\n if (!protocolAllowlist.has(scheme)) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(`Blocked Link with dangerous protocol: ${to}`)\n }\n return null\n }\n return to\n}\n\nfunction resolveIsActive(\n location: ParsedLocation,\n next: ParsedLocation,\n activeOptions: ActiveOptions | undefined,\n basepath: string,\n): boolean {\n const currentPath = removeTrailingSlash(location.pathname, basepath)\n const nextPath = removeTrailingSlash(next.pathname, basepath)\n\n // Both modes compare normalized paths; fuzzy matches need a segment boundary.\n if (\n activeOptions?.exact\n ? currentPath !== nextPath\n : !(\n currentPath.startsWith(nextPath) &&\n (currentPath.length === nextPath.length ||\n currentPath[nextPath.length] === '/')\n )\n ) {\n return false\n }\n\n if (activeOptions?.includeSearch ?? true) {\n const searchTest = deepEqual(\n location.search,\n next.search,\n !activeOptions?.exact,\n activeOptions?.explicitUndefined,\n )\n if (!searchTest) {\n return false\n }\n }\n\n return !activeOptions?.includeHash || location.hash === next.hash\n}\n\n/**\n * Build anchor-like props for declarative navigation and preloading.\n *\n * Returns stable `href`, event handlers and accessibility props derived from\n * router options and active state. Used internally by `Link` and custom links.\n *\n * Options cover `to`, `params`, `search`, `hash`, `state`, `preload`,\n * `activeProps`, `inactiveProps`, and more.\n *\n * @returns React anchor props suitable for `<a>` or custom components.\n * @link https://tanstack.com/router/latest/docs/framework/react/api/router/useLinkPropsHook\n */\nexport function useLinkProps<\n TRouter extends AnyRouter = RegisteredRouter,\n const TFrom extends string = string,\n const TTo extends string | undefined = undefined,\n const TMaskFrom extends string = TFrom,\n const TMaskTo extends string = '',\n>(\n options: UseLinkPropsOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,\n forwardedRef?: React.ForwardedRef<Element>,\n): React.ComponentPropsWithRef<'a'>\n/**\n * `host` is what the props are rendered on: `'a'` for `Link` or the component\n * given to `createLink`. `Link` never renders `type` and an anchor never\n * receives `disabled`, so those are left out here rather than copied away\n * from the result in the component. Stripped from the public declarations.\n *\n * @internal\n */\nexport function useLinkProps<\n TRouter extends AnyRouter = RegisteredRouter,\n const TFrom extends string = string,\n const TTo extends string | undefined = undefined,\n const TMaskFrom extends string = TFrom,\n const TMaskTo extends string = '',\n>(\n options: UseLinkPropsOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,\n forwardedRef: React.ForwardedRef<Element> | undefined,\n host: 'a' | React.ElementType,\n): React.ComponentPropsWithRef<'a'>\nexport function useLinkProps<\n TRouter extends AnyRouter = RegisteredRouter,\n const TFrom extends string = string,\n const TTo extends string | undefined = undefined,\n const TMaskFrom extends string = TFrom,\n const TMaskTo extends string = '',\n>(\n options: UseLinkPropsOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,\n forwardedRef?: React.ForwardedRef<Element>,\n host?: 'a' | React.ElementType,\n): React.ComponentPropsWithRef<'a'> {\n const router = useRouter()\n\n // ==========================================================================\n // SERVER EARLY RETURN\n // On the server, we return static props without any event handlers,\n // effects, or client-side interactivity.\n //\n // For SSR parity (to avoid hydration errors), we still compute the link's\n // active status on the server, but we avoid creating any router-state\n // subscriptions by reading from the location store directly.\n //\n // Note: `location.hash` is not available on the server.\n // ==========================================================================\n // The expression must stay inlined in the `if` so bundlers fold the\n // browser-build constant `isServer = false` and drop this server block,\n // together with `getServerLinkProps` and the key sets only it references.\n if (isServer ?? router.isServer) {\n return getServerLinkProps(router, options, forwardedRef, host)\n }\n\n // ==========================================================================\n // CLIENT-ONLY CODE\n // Everything below this point only runs on the client. The `isServer` check\n // above is a compile-time constant that bundlers use for dead code elimination,\n // so this entire section is removed from server bundles.\n //\n // We disable the rules-of-hooks lint rule because these hooks appear after\n // an early return. This is safe because:\n // 1. `isServer` is a compile-time constant from conditional exports\n // 2. In server bundles, this code is completely eliminated by the bundler\n // 3. In client bundles, `isServer` is `false`, so the early return never executes\n // ==========================================================================\n\n // The link's own ref: the element for the viewport observer and the holder\n // of a pending intent timer. A forwarded ref is filled alongside it by one\n // callback, memoized on the forwarded ref so React re-attaches it (and\n // notifies the consumer) only when their ref changes, not on every render.\n // A cleanup returned by a consumer callback is passed through to React.\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const innerRef: LinkRef = React.useRef<Element>(null)\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const mergedRef = React.useCallback(\n (element: Element | null) => {\n innerRef.current = element\n if (typeof forwardedRef === 'function') {\n return forwardedRef(element)\n }\n if (forwardedRef) {\n forwardedRef.current = element\n }\n return undefined\n },\n [forwardedRef],\n )\n\n const {\n activeOptions,\n to,\n preload: userPreload,\n preloadDelay: userPreloadDelay,\n disabled,\n target,\n onClick,\n onBlur,\n onFocus,\n onMouseEnter,\n onMouseLeave,\n onTouchStart,\n } = options as typeof options & { to?: string }\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const [stableSearch, stableParams, stableActiveOptions] = useStableValues(\n options.search,\n options.params,\n activeOptions,\n )\n // The links of a route share its location source. Links outside every\n // match share one that never departs.\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const routeId = React.useContext(matchContext)\n const scope = ((router.stores._linkScopes ??= {})[routeId!] ??=\n createLinkScope(router, routeId))\n\n // `dest` is the link's own copy of the options from the render that last\n // changed the link: one stable object per link lets the router reuse\n // location-independent results. It also carries the location the link\n // displays, so clicks and preloads go where its href points.\n // The snapshot depends only on the scope's location: it derives once per\n // location so React can bail out on an unchanged state, and derives eagerly\n // inside the scope's notification.\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const [dest, getLinkState, getHydrationState] = React.useMemo(() => {\n // Direct destinations and the router's allowlist are stable for this snapshot.\n const directExternalLink = resolveExternalLink(to, router.protocolAllowlist)\n if (directExternalLink !== undefined) {\n const state: LinkState = [directExternalLink ?? undefined]\n return [options, () => state] as const\n }\n\n const dest = { ...options } as any\n let source: ParsedLocation | undefined\n let state: LinkState\n let hydrationState: LinkState | undefined\n\n const getLinkState = (): LinkState => {\n // Read the location once and record it only after deriving: a\n // derivation that throws is retried instead of caching its state.\n const location = scope[0 /* getSource */]()\n if (location !== source) {\n dest._fromLocation = location\n const next = router.buildLocation(dest)\n\n // History formatters can depend on the current browser URL (hash history).\n const href = getHrefOption(next, router, disabled)\n // Internal and disabled links can be active; external and blocked\n // links have no active state.\n const isActive =\n disabled || (href && !getUrlScheme(href))\n ? resolveIsActive(\n location,\n next,\n stableActiveOptions,\n router.basepath,\n )\n : undefined\n // Keep the state while it is equal so React can bail out.\n if (\n !state ||\n state[0 /* href */] !== href ||\n state[1 /* isActive */] !== isActive\n ) {\n state = [href, isActive]\n }\n source = location\n }\n return state\n }\n\n return [\n dest,\n getLinkState,\n // Hydration renders the server's markup, which has no hash, so an active\n // hash-sensitive link hydrates inactive and React rerenders it after.\n stableActiveOptions?.includeHash &&\n ((): LinkState => {\n const state = getLinkState()\n return state[1 /* isActive */]\n ? (hydrationState ??= [state[0 /* href */], false])\n : state\n }),\n ] as const\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n router,\n scope,\n options.from,\n options.hash,\n options.href,\n to,\n stableSearch,\n stableParams,\n options.state,\n options.mask,\n options.unsafeRelative,\n stableActiveOptions,\n disabled,\n ])\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const [href, isActive] = React.useSyncExternalStore(\n scope[1 /* subscribe */],\n getLinkState,\n getHydrationState || getLinkState,\n )\n const externalLink = isActive === undefined && href\n const linkDisabled = disabled || href === undefined\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const hasRenderFetched = React.useRef(false)\n\n const preload =\n options.reloadDocument || externalLink || linkDisabled\n ? false\n : (userPreload ?? router.options.defaultPreload)\n const preloadDelay =\n userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0\n\n // `preloadRoute` builds the location itself and only reads the options, so\n // `dest` goes through as-is.\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const enqueuePreload = React.useCallback(\n (e?: React.MouseEvent | React.FocusEvent | IntersectionObserverEntry) => {\n const isIntersecting = (e as IntersectionObserverEntry | undefined)\n ?.isIntersecting\n if (!(isIntersecting ?? preload === 'intent')) {\n if (isIntersecting === false) {\n cancelPreload(innerRef)\n }\n return\n }\n\n if (!preloadDelay) {\n preloadLink(router, dest)\n return\n }\n\n innerRef.t ??= setTimeout(() => {\n innerRef.t = undefined\n preloadLink(router, dest)\n }, preloadDelay)\n },\n [router, dest, innerRef, preload, preloadDelay],\n )\n\n // Preload side effects: `render` preloads once per link, `viewport` watches\n // the element. The cleanup also cancels a pending intent timer.\n // eslint-disable-next-line react-hooks/rules-of-hooks\n React.useEffect(() => {\n // Disabled preloading creates neither an observer nor an intent timer.\n if (!preload) {\n return\n }\n if (preload === 'render' && !hasRenderFetched.current) {\n hasRenderFetched.current = true\n preloadLink(router, dest)\n }\n let active = true\n let observer: IntersectionObserver | undefined\n if (\n preload === 'viewport' &&\n innerRef.current &&\n typeof IntersectionObserver === 'function'\n ) {\n observer = new IntersectionObserver(\n (entries) => {\n // A queued observer notification can arrive after disconnect().\n if (active) {\n enqueuePreload(entries.pop())\n }\n },\n { rootMargin: '100px' },\n )\n observer.observe(innerRef.current)\n }\n return () => {\n active = false\n observer?.disconnect()\n cancelPreload(innerRef)\n }\n }, [router, dest, preload, enqueuePreload, innerRef])\n\n const props = collectElementProps(options, host)\n props.ref = forwardedRef ? mergedRef : innerRef\n // External links get no router behavior: element props pass through as given.\n if (externalLink) {\n props.href = externalLink\n return props\n }\n\n // The click handler\n const handleClick = (e: React.MouseEvent) => {\n // The element's own target attribute is the fallback.\n const effectiveTarget =\n target ??\n (e.currentTarget as HTMLAnchorElement | SVGAElement).getAttribute(\n 'target',\n )\n\n if (\n !linkDisabled &&\n !(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) &&\n !e.defaultPrevented &&\n (!effectiveTarget || effectiveTarget === '_self') &&\n e.button === 0\n ) {\n e.preventDefault()\n cancelPreload(innerRef)\n\n // The current options carry the navigation controls; `dest` only\n // contributes the location the link displays. Navigation builds again\n // to run `validateSearch` before committing.\n router.navigate({ ...options, _fromLocation: dest._fromLocation })\n }\n }\n\n const handleTouchStart = () => {\n if (preload === 'intent') {\n preloadLink(router, dest)\n }\n }\n\n const handleLeave = () => {\n if (preload === 'intent') {\n cancelPreload(innerRef)\n }\n }\n\n props.onClick = composeHandlers(onClick, handleClick)\n props.onBlur = composeHandlers(onBlur, handleLeave)\n props.onFocus = composeHandlers(onFocus, enqueuePreload)\n props.onMouseEnter = composeHandlers(onMouseEnter, enqueuePreload)\n props.onMouseLeave = composeHandlers(onMouseLeave, handleLeave)\n props.onTouchStart = composeHandlers(onTouchStart, handleTouchStart)\n return applyLinkState(props, options, isActive, href, linkDisabled, host)\n}\n\nconst STATIC_EMPTY_OBJECT = {}\nconst STATIC_ACTIVE_OBJECT = { className: 'active' }\n// Options the router consumes; they never reach the element. Every other\n// option is an element prop and passes through.\nconst ROUTER_OPTION_KEYS = /* @__PURE__ */ new Set([\n 'to',\n 'params',\n 'search',\n 'hash',\n 'state',\n 'mask',\n 'from',\n 'unsafeRelative',\n 'reloadDocument',\n 'preload',\n 'preloadDelay',\n 'preloadIntentProximity',\n 'hashScrollIntoView',\n 'replace',\n 'startTransition',\n 'resetScroll',\n 'viewTransition',\n 'ignoreBlocker',\n 'activeProps',\n 'inactiveProps',\n 'activeOptions',\n '_asChild',\n])\n\n// Copies the element props. An object rest would test every key against the\n// whole exclusion list; the key set is much cheaper. `Link` hosts never render\n// `type`, and an anchor has no `disabled` attribute.\nfunction collectElementProps(\n options: object,\n host: 'a' | React.ElementType | undefined,\n): Record<string, unknown> {\n const props: Record<string, unknown> = {}\n for (const key in options) {\n if (\n ROUTER_OPTION_KEYS.has(key) ||\n (key === 'type' && host !== undefined) ||\n (key === 'disabled' && host === 'a')\n ) {\n continue\n }\n props[key] = (options as Record<string, unknown>)[key]\n }\n return props\n}\n\n// Finishes a router-controlled link: the selected state props, then the\n// routing attributes. This is the one place that defines precedence: state\n// props override element props, `ref` and handlers; `href`, `disabled`,\n// `target` and the merged class and style always win.\nfunction applyLinkState(\n props: Record<string, unknown>,\n options: {\n activeProps?: unknown\n inactiveProps?: unknown\n className?: string\n style?: React.CSSProperties\n target?: string\n },\n isActive: boolean | undefined,\n href: string | undefined,\n linkDisabled: boolean,\n host: 'a' | React.ElementType | undefined,\n): React.ComponentPropsWithRef<'a'> {\n const { activeProps, inactiveProps, className, style, target } = options\n const stateProps: React.HTMLAttributes<HTMLAnchorElement> =\n functionalUpdate((isActive ? activeProps : inactiveProps) as any, {}) ??\n (isActive ? STATIC_ACTIVE_OBJECT : STATIC_EMPTY_OBJECT)\n Object.assign(props, stateProps)\n props.href = href\n if (host !== 'a') {\n props.disabled = linkDisabled\n }\n props.target = target\n // Merge class and style with the state's. Links without either keep their\n // props as given and carry no `undefined` keys.\n const stateStyle = stateProps.style\n if (style || stateStyle) {\n props.style =\n style && stateStyle ? { ...style, ...stateStyle } : style || stateStyle\n }\n const stateClassName = stateProps.className\n if (className || stateClassName) {\n props.className = className\n ? stateClassName\n ? `${className} ${stateClassName}`\n : className\n : stateClassName\n }\n if (linkDisabled) {\n props.role = 'link'\n props['aria-disabled'] = true\n }\n if (isActive) {\n props['data-status'] = 'active'\n props['aria-current'] = 'page'\n }\n return props\n}\n\n// Server render of a Link: static props only, no hooks. Only server bundles\n// keep this function; the `isServer` check that calls it folds away on the client.\nfunction getServerLinkProps(\n router: AnyRouter,\n options: any,\n forwardedRef: React.ForwardedRef<Element> | undefined,\n host: 'a' | React.ElementType | undefined,\n): React.ComponentPropsWithRef<'a'> {\n const { to, disabled, activeOptions } = options as {\n to: string | undefined\n disabled: boolean | undefined\n activeOptions: ActiveOptions | undefined\n }\n\n const directExternalLink = resolveExternalLink(to, router.protocolAllowlist)\n\n // Direct-scheme links need no route resolution. Blocked links still use\n // the shared inactive-prop merge so their server and client markup agree.\n const next =\n directExternalLink === undefined ? router.buildLocation(options) : undefined\n\n const hrefOption = next\n ? getHrefOption(next, router, disabled)\n : (directExternalLink ?? undefined)\n const linkDisabled = disabled || !hrefOption\n\n const externalLink =\n directExternalLink ??\n (hrefOption && getUrlScheme(hrefOption) ? hrefOption : undefined)\n\n const props = collectElementProps(options, host)\n props.ref = forwardedRef\n if (externalLink) {\n props.href = externalLink\n return props\n }\n\n const blockedLink = !disabled && !hrefOption\n // Hash is not available on the server: hash-sensitive links are inactive.\n const isActive =\n !!next &&\n !blockedLink &&\n !activeOptions?.includeHash &&\n resolveIsActive(\n router.stores.location.get(),\n next,\n activeOptions,\n router.basepath,\n )\n return applyLinkState(\n props,\n options,\n isActive,\n hrefOption,\n linkDisabled,\n host,\n )\n}\n\n// A Link's own ref also holds its pending intent preload timer.\ntype LinkRef = React.RefObject<Element | null> & {\n t?: ReturnType<typeof setTimeout>\n}\nconst cancelPreload = (ref: LinkRef) => {\n clearTimeout(ref.t)\n ref.t = undefined\n}\n\nexport const composeHandlers = (\n first: React.EventHandler<any> | undefined,\n second: React.EventHandler<any>,\n) => {\n if (!first) {\n return second\n }\n\n // The first guard skips user handlers for already-prevented events; the second\n // lets user handlers prevent the internal handler from running.\n return (event: React.SyntheticEvent) =>\n event.defaultPrevented ||\n (first(event), event.defaultPrevented || second(event))\n}\n\nfunction getHrefOption(\n next: ParsedLocation,\n router: AnyRouter,\n disabled: boolean | undefined,\n) {\n if (disabled) {\n return undefined\n }\n const location = next.maskedLocation ?? next\n // A rewritten external URL must bypass history's relative-path formatting.\n const href = location.external\n ? location.publicHref\n : router.history.createHref(location.publicHref) || '/'\n if (\n (location.external || href !== location.publicHref) &&\n isDangerousProtocol(href, router.protocolAllowlist)\n ) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(`Blocked Link with dangerous protocol: ${href}`)\n }\n return undefined\n }\n return href\n}\n\ntype UseLinkReactProps<TComp> = TComp extends keyof React.JSX.IntrinsicElements\n ? React.JSX.IntrinsicElements[TComp]\n : TComp extends React.ComponentType<any>\n ? React.ComponentPropsWithoutRef<TComp> &\n React.RefAttributes<React.ComponentRef<TComp>>\n : never\n\nexport type UseLinkPropsOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends RoutePaths<TRouter['routeTree']> | string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,\n TMaskTo extends string = '.',\n> = ActiveLinkOptions<'a', TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &\n UseLinkReactProps<'a'>\n\nexport type ActiveLinkOptions<\n TComp = 'a',\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '.',\n> = LinkOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &\n ActiveLinkOptionProps<TComp>\n\ntype ActiveLinkProps<TComp> = Partial<\n LinkComponentReactProps<TComp> & {\n [key: `data-${string}`]: unknown\n }\n>\n\nexport interface ActiveLinkOptionProps<TComp = 'a'> {\n /**\n * A function that returns additional props for the `active` state of this link.\n * These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)\n */\n activeProps?: ActiveLinkProps<TComp> | (() => ActiveLinkProps<TComp>)\n /**\n * A function that returns additional props for the `inactive` state of this link.\n * These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)\n */\n inactiveProps?: ActiveLinkProps<TComp> | (() => ActiveLinkProps<TComp>)\n}\n\nexport type LinkProps<\n TComp = 'a',\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '.',\n> = ActiveLinkOptions<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &\n LinkPropsChildren\n\nexport interface LinkPropsChildren {\n // If a function is passed as a child, it will be given the `isActive` boolean to aid in further styling on the element it returns\n children?:\n | React.ReactNode\n | ((state: { isActive: boolean }) => React.ReactNode)\n}\n\ntype LinkComponentReactProps<TComp> = Omit<\n UseLinkReactProps<TComp>,\n keyof CreateLinkProps\n>\n\nexport type LinkComponentProps<\n TComp = 'a',\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '.',\n> = LinkComponentReactProps<TComp> &\n LinkProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>\n\nexport type CreateLinkProps = LinkProps<\n any,\n any,\n string,\n string,\n string,\n string\n>\n\nexport type LinkComponent<\n in out TComp,\n in out TDefaultFrom extends string = string,\n> = <\n TRouter extends AnyRouter = RegisteredRouter,\n const TFrom extends string = TDefaultFrom,\n const TTo extends string | undefined = undefined,\n const TMaskFrom extends string = TFrom,\n const TMaskTo extends string = '',\n>(\n props: LinkComponentProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,\n) => React.ReactElement\n\nexport interface LinkComponentRoute<\n in out TDefaultFrom extends string = string,\n> {\n defaultFrom: TDefaultFrom;\n <\n TRouter extends AnyRouter = RegisteredRouter,\n const TTo extends string | undefined = undefined,\n const TMaskTo extends string = '',\n >(\n props: LinkComponentProps<\n 'a',\n TRouter,\n this['defaultFrom'],\n TTo,\n this['defaultFrom'],\n TMaskTo\n >,\n ): React.ReactElement\n}\n\n/**\n * Creates a typed Link-like component that preserves TanStack Router's\n * navigation semantics and type-safety while delegating rendering to the\n * provided host component.\n *\n * Useful for integrating design system anchors/buttons while keeping\n * router-aware props (eg. `to`, `params`, `search`, `preload`).\n *\n * @param Comp The host component to render (eg. a design-system Link/Button)\n * @returns A router-aware component with the same API as `Link`.\n * @link https://tanstack.com/router/latest/docs/framework/react/guide/custom-link\n */\nexport function createLink<const TComp>(\n Comp: Constrain<TComp, any, (props: CreateLinkProps) => ReactNode>,\n): LinkComponent<TComp> {\n return React.forwardRef(function CreatedLink(props, ref) {\n return <Link {...(props as any)} _asChild={Comp} ref={ref} />\n }) as any\n}\n\n/**\n * A strongly-typed anchor component for declarative navigation.\n * Handles path, search, hash and state updates with optional route preloading\n * and active-state styling.\n *\n * Props:\n * - `preload`: Controls route preloading (eg. 'intent', 'render', 'viewport', true/false)\n * - `preloadDelay`: Delay in ms before preloading on focus, hover, or viewport entry\n * - `activeProps`/`inactiveProps`: Additional props merged when link is active/inactive\n * - `resetScroll`/`hashScrollIntoView`: Control scroll behavior on navigation\n * - `viewTransition`/`startTransition`: Use View Transitions/React transitions for navigation\n * - `ignoreBlocker`: Bypass registered blockers\n *\n * @returns An anchor-like element that navigates without full page reloads.\n * @link https://tanstack.com/router/latest/docs/framework/react/api/router/linkComponent\n */\nexport const Link: LinkComponent<'a'> = React.memo(\n React.forwardRef<Element, any>((props, ref) => {\n const host = props._asChild || 'a'\n const linkProps = useLinkProps(props as any, ref, host)\n\n const children =\n typeof props.children === 'function'\n ? props.children({\n isActive: (linkProps as any)['data-status'] === 'active',\n })\n : props.children\n\n return React.createElement(host, linkProps, children)\n }),\n areLinkPropsEqual,\n) as any\n\n// A Link's output depends only on its props, the router context and its\n// route's location source, which React tracks for memoized components, so a parent\n// re-render with equal props can skip it. Router options are compared by\n// value: destinations are usually inline object literals. Element props\n// (`children`, handlers, `style`, ...) are compared by reference only, since\n// they may hold arbitrary (even cyclic) data.\nfunction areLinkPropsEqual(\n prev: Record<string, unknown>,\n next: Record<string, unknown>,\n): boolean {\n let extraKeys = 0\n for (const key in next) {\n extraKeys++\n if (prev[key] === next[key]) {\n continue\n }\n if (\n !ROUTER_OPTION_KEYS.has(key) ||\n !deepEqual(prev[key], next[key], false, true)\n ) {\n return false\n }\n }\n for (const _key in prev) {\n extraKeys--\n }\n return extraKeys === 0\n}\n\nexport type LinkOptionsFnOptions<\n TOptions,\n TComp,\n TRouter extends AnyRouter = RegisteredRouter,\n> =\n TOptions extends ReadonlyArray<any>\n ? ValidateLinkOptionsArray<TRouter, TOptions, string, TComp>\n : ValidateLinkOptions<TRouter, TOptions, string, TComp>\n\nexport type LinkOptionsFn<TComp> = <\n const TOptions,\n TRouter extends AnyRouter = RegisteredRouter,\n>(\n options: LinkOptionsFnOptions<TOptions, TComp, TRouter>,\n) => TOptions\n\n/**\n * Validate and reuse navigation options for `Link`, `navigate` or `redirect`.\n * Accepts a literal options object and returns it typed for later spreading.\n * @example\n * const opts = linkOptions({ to: '/dashboard', search: { tab: 'home' } })\n * @link https://tanstack.com/router/latest/docs/framework/react/api/router/linkOptions\n */\nexport const linkOptions: LinkOptionsFn<'a'> = (options) => {\n return options as any\n}\n\n/**\n * Type-check a literal object for use with `Link`, `navigate` or `redirect`.\n * Use to validate and reuse navigation options across your app.\n * @example\n * const opts = linkOptions({ to: '/dashboard', search: { tab: 'home' } })\n * @link https://tanstack.com/router/latest/docs/framework/react/api/router/linkOptions\n */\n"],"mappings":"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"}