@depup/tanstack__react-router 1.166.4-depup.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (363) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +32 -0
  3. package/dist/cjs/Asset.cjs +177 -0
  4. package/dist/cjs/Asset.cjs.map +1 -0
  5. package/dist/cjs/Asset.d.cts +5 -0
  6. package/dist/cjs/CatchBoundary.cjs +114 -0
  7. package/dist/cjs/CatchBoundary.cjs.map +1 -0
  8. package/dist/cjs/CatchBoundary.d.cts +12 -0
  9. package/dist/cjs/ClientOnly.cjs +21 -0
  10. package/dist/cjs/ClientOnly.cjs.map +1 -0
  11. package/dist/cjs/ClientOnly.d.cts +49 -0
  12. package/dist/cjs/HeadContent.cjs +15 -0
  13. package/dist/cjs/HeadContent.cjs.map +1 -0
  14. package/dist/cjs/HeadContent.d.cts +6 -0
  15. package/dist/cjs/HeadContent.dev.cjs +41 -0
  16. package/dist/cjs/HeadContent.dev.cjs.map +1 -0
  17. package/dist/cjs/HeadContent.dev.d.cts +10 -0
  18. package/dist/cjs/Match.cjs +255 -0
  19. package/dist/cjs/Match.cjs.map +1 -0
  20. package/dist/cjs/Match.d.cts +14 -0
  21. package/dist/cjs/Matches.cjs +138 -0
  22. package/dist/cjs/Matches.cjs.map +1 -0
  23. package/dist/cjs/Matches.d.cts +68 -0
  24. package/dist/cjs/RouterProvider.cjs +32 -0
  25. package/dist/cjs/RouterProvider.cjs.map +1 -0
  26. package/dist/cjs/RouterProvider.d.cts +23 -0
  27. package/dist/cjs/SafeFragment.cjs +8 -0
  28. package/dist/cjs/SafeFragment.cjs.map +1 -0
  29. package/dist/cjs/SafeFragment.d.cts +1 -0
  30. package/dist/cjs/ScriptOnce.cjs +22 -0
  31. package/dist/cjs/ScriptOnce.cjs.map +1 -0
  32. package/dist/cjs/ScriptOnce.d.cts +6 -0
  33. package/dist/cjs/Scripts.cjs +56 -0
  34. package/dist/cjs/Scripts.cjs.map +1 -0
  35. package/dist/cjs/Scripts.d.cts +5 -0
  36. package/dist/cjs/ScrollRestoration.cjs +38 -0
  37. package/dist/cjs/ScrollRestoration.cjs.map +1 -0
  38. package/dist/cjs/ScrollRestoration.d.cts +14 -0
  39. package/dist/cjs/Transitioner.cjs +119 -0
  40. package/dist/cjs/Transitioner.cjs.map +1 -0
  41. package/dist/cjs/Transitioner.d.cts +1 -0
  42. package/dist/cjs/awaited.cjs +51 -0
  43. package/dist/cjs/awaited.cjs.map +1 -0
  44. package/dist/cjs/awaited.d.cts +14 -0
  45. package/dist/cjs/fileRoute.cjs +109 -0
  46. package/dist/cjs/fileRoute.cjs.map +1 -0
  47. package/dist/cjs/fileRoute.d.cts +87 -0
  48. package/dist/cjs/headContentUtils.cjs +185 -0
  49. package/dist/cjs/headContentUtils.cjs.map +1 -0
  50. package/dist/cjs/headContentUtils.d.cts +7 -0
  51. package/dist/cjs/history.d.cts +8 -0
  52. package/dist/cjs/index.cjs +241 -0
  53. package/dist/cjs/index.cjs.map +1 -0
  54. package/dist/cjs/index.d.cts +51 -0
  55. package/dist/cjs/index.dev.cjs +241 -0
  56. package/dist/cjs/index.dev.cjs.map +1 -0
  57. package/dist/cjs/index.dev.d.cts +2 -0
  58. package/dist/cjs/lazyRouteComponent.cjs +70 -0
  59. package/dist/cjs/lazyRouteComponent.cjs.map +1 -0
  60. package/dist/cjs/lazyRouteComponent.d.cts +11 -0
  61. package/dist/cjs/link.cjs +573 -0
  62. package/dist/cjs/link.cjs.map +1 -0
  63. package/dist/cjs/link.d.cts +98 -0
  64. package/dist/cjs/matchContext.cjs +27 -0
  65. package/dist/cjs/matchContext.cjs.map +1 -0
  66. package/dist/cjs/matchContext.d.cts +3 -0
  67. package/dist/cjs/not-found.cjs +38 -0
  68. package/dist/cjs/not-found.cjs.map +1 -0
  69. package/dist/cjs/not-found.d.cts +9 -0
  70. package/dist/cjs/renderRouteNotFound.cjs +22 -0
  71. package/dist/cjs/renderRouteNotFound.cjs.map +1 -0
  72. package/dist/cjs/renderRouteNotFound.d.cts +10 -0
  73. package/dist/cjs/route.cjs +198 -0
  74. package/dist/cjs/route.cjs.map +1 -0
  75. package/dist/cjs/route.d.cts +142 -0
  76. package/dist/cjs/router.cjs +22 -0
  77. package/dist/cjs/router.cjs.map +1 -0
  78. package/dist/cjs/router.d.cts +83 -0
  79. package/dist/cjs/routerContext.cjs +23 -0
  80. package/dist/cjs/routerContext.cjs.map +1 -0
  81. package/dist/cjs/routerContext.d.cts +3 -0
  82. package/dist/cjs/scroll-restoration.cjs +39 -0
  83. package/dist/cjs/scroll-restoration.cjs.map +1 -0
  84. package/dist/cjs/scroll-restoration.d.cts +1 -0
  85. package/dist/cjs/ssr/RouterClient.cjs +25 -0
  86. package/dist/cjs/ssr/RouterClient.cjs.map +1 -0
  87. package/dist/cjs/ssr/RouterClient.d.cts +4 -0
  88. package/dist/cjs/ssr/RouterServer.cjs +9 -0
  89. package/dist/cjs/ssr/RouterServer.cjs.map +1 -0
  90. package/dist/cjs/ssr/RouterServer.d.cts +4 -0
  91. package/dist/cjs/ssr/client.cjs +12 -0
  92. package/dist/cjs/ssr/client.cjs.map +1 -0
  93. package/dist/cjs/ssr/client.d.cts +2 -0
  94. package/dist/cjs/ssr/defaultRenderHandler.cjs +15 -0
  95. package/dist/cjs/ssr/defaultRenderHandler.cjs.map +1 -0
  96. package/dist/cjs/ssr/defaultRenderHandler.d.cts +1 -0
  97. package/dist/cjs/ssr/defaultStreamHandler.cjs +16 -0
  98. package/dist/cjs/ssr/defaultStreamHandler.cjs.map +1 -0
  99. package/dist/cjs/ssr/defaultStreamHandler.d.cts +1 -0
  100. package/dist/cjs/ssr/renderRouterToStream.cjs +73 -0
  101. package/dist/cjs/ssr/renderRouterToStream.cjs.map +1 -0
  102. package/dist/cjs/ssr/renderRouterToStream.d.cts +8 -0
  103. package/dist/cjs/ssr/renderRouterToString.cjs +31 -0
  104. package/dist/cjs/ssr/renderRouterToString.cjs.map +1 -0
  105. package/dist/cjs/ssr/renderRouterToString.d.cts +7 -0
  106. package/dist/cjs/ssr/serializer.d.cts +6 -0
  107. package/dist/cjs/ssr/server.cjs +20 -0
  108. package/dist/cjs/ssr/server.cjs.map +1 -0
  109. package/dist/cjs/ssr/server.d.cts +6 -0
  110. package/dist/cjs/structuralSharing.d.cts +8 -0
  111. package/dist/cjs/typePrimitives.d.cts +16 -0
  112. package/dist/cjs/useBlocker.cjs +171 -0
  113. package/dist/cjs/useBlocker.cjs.map +1 -0
  114. package/dist/cjs/useBlocker.d.cts +66 -0
  115. package/dist/cjs/useCanGoBack.cjs +8 -0
  116. package/dist/cjs/useCanGoBack.cjs.map +1 -0
  117. package/dist/cjs/useCanGoBack.d.cts +1 -0
  118. package/dist/cjs/useLoaderData.cjs +15 -0
  119. package/dist/cjs/useLoaderData.cjs.map +1 -0
  120. package/dist/cjs/useLoaderData.d.cts +19 -0
  121. package/dist/cjs/useLoaderDeps.cjs +14 -0
  122. package/dist/cjs/useLoaderDeps.cjs.map +1 -0
  123. package/dist/cjs/useLoaderDeps.d.cts +19 -0
  124. package/dist/cjs/useLocation.cjs +10 -0
  125. package/dist/cjs/useLocation.cjs.map +1 -0
  126. package/dist/cjs/useLocation.d.cts +18 -0
  127. package/dist/cjs/useMatch.cjs +47 -0
  128. package/dist/cjs/useMatch.cjs.map +1 -0
  129. package/dist/cjs/useMatch.d.cts +14 -0
  130. package/dist/cjs/useNavigate.cjs +49 -0
  131. package/dist/cjs/useNavigate.cjs.map +1 -0
  132. package/dist/cjs/useNavigate.d.cts +28 -0
  133. package/dist/cjs/useParams.cjs +17 -0
  134. package/dist/cjs/useParams.cjs.map +1 -0
  135. package/dist/cjs/useParams.d.cts +21 -0
  136. package/dist/cjs/useRouteContext.cjs +11 -0
  137. package/dist/cjs/useRouteContext.cjs.map +1 -0
  138. package/dist/cjs/useRouteContext.d.cts +3 -0
  139. package/dist/cjs/useRouter.cjs +32 -0
  140. package/dist/cjs/useRouter.cjs.map +1 -0
  141. package/dist/cjs/useRouter.d.cts +14 -0
  142. package/dist/cjs/useRouterState.cjs +38 -0
  143. package/dist/cjs/useRouterState.cjs.map +1 -0
  144. package/dist/cjs/useRouterState.d.cts +20 -0
  145. package/dist/cjs/useSearch.cjs +16 -0
  146. package/dist/cjs/useSearch.cjs.map +1 -0
  147. package/dist/cjs/useSearch.d.cts +21 -0
  148. package/dist/cjs/utils.cjs +62 -0
  149. package/dist/cjs/utils.cjs.map +1 -0
  150. package/dist/cjs/utils.d.cts +54 -0
  151. package/dist/esm/Asset.d.ts +5 -0
  152. package/dist/esm/Asset.js +160 -0
  153. package/dist/esm/Asset.js.map +1 -0
  154. package/dist/esm/CatchBoundary.d.ts +12 -0
  155. package/dist/esm/CatchBoundary.js +97 -0
  156. package/dist/esm/CatchBoundary.js.map +1 -0
  157. package/dist/esm/ClientOnly.d.ts +49 -0
  158. package/dist/esm/ClientOnly.js +21 -0
  159. package/dist/esm/ClientOnly.js.map +1 -0
  160. package/dist/esm/HeadContent.d.ts +6 -0
  161. package/dist/esm/HeadContent.dev.d.ts +10 -0
  162. package/dist/esm/HeadContent.dev.js +25 -0
  163. package/dist/esm/HeadContent.dev.js.map +1 -0
  164. package/dist/esm/HeadContent.js +15 -0
  165. package/dist/esm/HeadContent.js.map +1 -0
  166. package/dist/esm/Match.d.ts +14 -0
  167. package/dist/esm/Match.js +238 -0
  168. package/dist/esm/Match.js.map +1 -0
  169. package/dist/esm/Matches.d.ts +68 -0
  170. package/dist/esm/Matches.js +121 -0
  171. package/dist/esm/Matches.js.map +1 -0
  172. package/dist/esm/RouterProvider.d.ts +23 -0
  173. package/dist/esm/RouterProvider.js +32 -0
  174. package/dist/esm/RouterProvider.js.map +1 -0
  175. package/dist/esm/SafeFragment.d.ts +1 -0
  176. package/dist/esm/SafeFragment.js +8 -0
  177. package/dist/esm/SafeFragment.js.map +1 -0
  178. package/dist/esm/ScriptOnce.d.ts +6 -0
  179. package/dist/esm/ScriptOnce.js +22 -0
  180. package/dist/esm/ScriptOnce.js.map +1 -0
  181. package/dist/esm/Scripts.d.ts +5 -0
  182. package/dist/esm/Scripts.js +56 -0
  183. package/dist/esm/Scripts.js.map +1 -0
  184. package/dist/esm/ScrollRestoration.d.ts +14 -0
  185. package/dist/esm/ScrollRestoration.js +38 -0
  186. package/dist/esm/ScrollRestoration.js.map +1 -0
  187. package/dist/esm/Transitioner.d.ts +1 -0
  188. package/dist/esm/Transitioner.js +102 -0
  189. package/dist/esm/Transitioner.js.map +1 -0
  190. package/dist/esm/awaited.d.ts +14 -0
  191. package/dist/esm/awaited.js +34 -0
  192. package/dist/esm/awaited.js.map +1 -0
  193. package/dist/esm/fileRoute.d.ts +87 -0
  194. package/dist/esm/fileRoute.js +109 -0
  195. package/dist/esm/fileRoute.js.map +1 -0
  196. package/dist/esm/headContentUtils.d.ts +7 -0
  197. package/dist/esm/headContentUtils.js +168 -0
  198. package/dist/esm/headContentUtils.js.map +1 -0
  199. package/dist/esm/history.d.ts +8 -0
  200. package/dist/esm/index.d.ts +51 -0
  201. package/dist/esm/index.dev.d.ts +2 -0
  202. package/dist/esm/index.dev.js +133 -0
  203. package/dist/esm/index.dev.js.map +1 -0
  204. package/dist/esm/index.js +133 -0
  205. package/dist/esm/index.js.map +1 -0
  206. package/dist/esm/lazyRouteComponent.d.ts +11 -0
  207. package/dist/esm/lazyRouteComponent.js +53 -0
  208. package/dist/esm/lazyRouteComponent.js.map +1 -0
  209. package/dist/esm/link.d.ts +98 -0
  210. package/dist/esm/link.js +556 -0
  211. package/dist/esm/link.js.map +1 -0
  212. package/dist/esm/matchContext.d.ts +3 -0
  213. package/dist/esm/matchContext.js +10 -0
  214. package/dist/esm/matchContext.js.map +1 -0
  215. package/dist/esm/not-found.d.ts +9 -0
  216. package/dist/esm/not-found.js +38 -0
  217. package/dist/esm/not-found.js.map +1 -0
  218. package/dist/esm/renderRouteNotFound.d.ts +10 -0
  219. package/dist/esm/renderRouteNotFound.js +22 -0
  220. package/dist/esm/renderRouteNotFound.js.map +1 -0
  221. package/dist/esm/route.d.ts +142 -0
  222. package/dist/esm/route.js +198 -0
  223. package/dist/esm/route.js.map +1 -0
  224. package/dist/esm/router.d.ts +83 -0
  225. package/dist/esm/router.js +22 -0
  226. package/dist/esm/router.js.map +1 -0
  227. package/dist/esm/routerContext.d.ts +3 -0
  228. package/dist/esm/routerContext.js +6 -0
  229. package/dist/esm/routerContext.js.map +1 -0
  230. package/dist/esm/scroll-restoration.d.ts +1 -0
  231. package/dist/esm/scroll-restoration.js +39 -0
  232. package/dist/esm/scroll-restoration.js.map +1 -0
  233. package/dist/esm/ssr/RouterClient.d.ts +4 -0
  234. package/dist/esm/ssr/RouterClient.js +25 -0
  235. package/dist/esm/ssr/RouterClient.js.map +1 -0
  236. package/dist/esm/ssr/RouterServer.d.ts +4 -0
  237. package/dist/esm/ssr/RouterServer.js +9 -0
  238. package/dist/esm/ssr/RouterServer.js.map +1 -0
  239. package/dist/esm/ssr/client.d.ts +2 -0
  240. package/dist/esm/ssr/client.js +6 -0
  241. package/dist/esm/ssr/client.js.map +1 -0
  242. package/dist/esm/ssr/defaultRenderHandler.d.ts +1 -0
  243. package/dist/esm/ssr/defaultRenderHandler.js +15 -0
  244. package/dist/esm/ssr/defaultRenderHandler.js.map +1 -0
  245. package/dist/esm/ssr/defaultStreamHandler.d.ts +1 -0
  246. package/dist/esm/ssr/defaultStreamHandler.js +16 -0
  247. package/dist/esm/ssr/defaultStreamHandler.js.map +1 -0
  248. package/dist/esm/ssr/renderRouterToStream.d.ts +8 -0
  249. package/dist/esm/ssr/renderRouterToStream.js +73 -0
  250. package/dist/esm/ssr/renderRouterToStream.js.map +1 -0
  251. package/dist/esm/ssr/renderRouterToString.d.ts +7 -0
  252. package/dist/esm/ssr/renderRouterToString.js +31 -0
  253. package/dist/esm/ssr/renderRouterToString.js.map +1 -0
  254. package/dist/esm/ssr/serializer.d.ts +6 -0
  255. package/dist/esm/ssr/server.d.ts +6 -0
  256. package/dist/esm/ssr/server.js +14 -0
  257. package/dist/esm/ssr/server.js.map +1 -0
  258. package/dist/esm/structuralSharing.d.ts +8 -0
  259. package/dist/esm/typePrimitives.d.ts +16 -0
  260. package/dist/esm/useBlocker.d.ts +66 -0
  261. package/dist/esm/useBlocker.js +154 -0
  262. package/dist/esm/useBlocker.js.map +1 -0
  263. package/dist/esm/useCanGoBack.d.ts +1 -0
  264. package/dist/esm/useCanGoBack.js +8 -0
  265. package/dist/esm/useCanGoBack.js.map +1 -0
  266. package/dist/esm/useLoaderData.d.ts +19 -0
  267. package/dist/esm/useLoaderData.js +15 -0
  268. package/dist/esm/useLoaderData.js.map +1 -0
  269. package/dist/esm/useLoaderDeps.d.ts +19 -0
  270. package/dist/esm/useLoaderDeps.js +14 -0
  271. package/dist/esm/useLoaderDeps.js.map +1 -0
  272. package/dist/esm/useLocation.d.ts +18 -0
  273. package/dist/esm/useLocation.js +10 -0
  274. package/dist/esm/useLocation.js.map +1 -0
  275. package/dist/esm/useMatch.d.ts +14 -0
  276. package/dist/esm/useMatch.js +30 -0
  277. package/dist/esm/useMatch.js.map +1 -0
  278. package/dist/esm/useNavigate.d.ts +28 -0
  279. package/dist/esm/useNavigate.js +32 -0
  280. package/dist/esm/useNavigate.js.map +1 -0
  281. package/dist/esm/useParams.d.ts +21 -0
  282. package/dist/esm/useParams.js +17 -0
  283. package/dist/esm/useParams.js.map +1 -0
  284. package/dist/esm/useRouteContext.d.ts +3 -0
  285. package/dist/esm/useRouteContext.js +11 -0
  286. package/dist/esm/useRouteContext.js.map +1 -0
  287. package/dist/esm/useRouter.d.ts +14 -0
  288. package/dist/esm/useRouter.js +15 -0
  289. package/dist/esm/useRouter.js.map +1 -0
  290. package/dist/esm/useRouterState.d.ts +20 -0
  291. package/dist/esm/useRouterState.js +38 -0
  292. package/dist/esm/useRouterState.js.map +1 -0
  293. package/dist/esm/useSearch.d.ts +21 -0
  294. package/dist/esm/useSearch.js +16 -0
  295. package/dist/esm/useSearch.js.map +1 -0
  296. package/dist/esm/utils.d.ts +54 -0
  297. package/dist/esm/utils.js +45 -0
  298. package/dist/esm/utils.js.map +1 -0
  299. package/dist/llms/index.d.ts +3 -0
  300. package/dist/llms/index.js +43 -0
  301. package/dist/llms/rules/api.d.ts +2 -0
  302. package/dist/llms/rules/api.js +4612 -0
  303. package/dist/llms/rules/guide.d.ts +2 -0
  304. package/dist/llms/rules/guide.js +10690 -0
  305. package/dist/llms/rules/installation.d.ts +2 -0
  306. package/dist/llms/rules/installation.js +1285 -0
  307. package/dist/llms/rules/routing.d.ts +2 -0
  308. package/dist/llms/rules/routing.js +1984 -0
  309. package/dist/llms/rules/setup-and-architecture.d.ts +2 -0
  310. package/dist/llms/rules/setup-and-architecture.js +920 -0
  311. package/package.json +142 -0
  312. package/src/Asset.tsx +219 -0
  313. package/src/CatchBoundary.tsx +120 -0
  314. package/src/ClientOnly.tsx +68 -0
  315. package/src/HeadContent.dev.tsx +46 -0
  316. package/src/HeadContent.tsx +22 -0
  317. package/src/Match.tsx +360 -0
  318. package/src/Matches.tsx +313 -0
  319. package/src/RouterProvider.tsx +92 -0
  320. package/src/SafeFragment.tsx +5 -0
  321. package/src/ScriptOnce.tsx +21 -0
  322. package/src/Scripts.tsx +80 -0
  323. package/src/ScrollRestoration.tsx +69 -0
  324. package/src/Transitioner.tsx +134 -0
  325. package/src/awaited.tsx +55 -0
  326. package/src/fileRoute.ts +313 -0
  327. package/src/headContentUtils.tsx +217 -0
  328. package/src/history.ts +9 -0
  329. package/src/index.dev.tsx +6 -0
  330. package/src/index.tsx +341 -0
  331. package/src/lazyRouteComponent.tsx +96 -0
  332. package/src/link.tsx +984 -0
  333. package/src/matchContext.tsx +8 -0
  334. package/src/not-found.tsx +43 -0
  335. package/src/renderRouteNotFound.tsx +35 -0
  336. package/src/route.tsx +740 -0
  337. package/src/router.ts +127 -0
  338. package/src/routerContext.tsx +4 -0
  339. package/src/scroll-restoration.tsx +45 -0
  340. package/src/ssr/RouterClient.tsx +22 -0
  341. package/src/ssr/RouterServer.tsx +9 -0
  342. package/src/ssr/client.ts +2 -0
  343. package/src/ssr/defaultRenderHandler.tsx +12 -0
  344. package/src/ssr/defaultStreamHandler.tsx +13 -0
  345. package/src/ssr/renderRouterToStream.tsx +90 -0
  346. package/src/ssr/renderRouterToString.tsx +36 -0
  347. package/src/ssr/serializer.ts +7 -0
  348. package/src/ssr/server.ts +6 -0
  349. package/src/structuralSharing.ts +47 -0
  350. package/src/typePrimitives.ts +84 -0
  351. package/src/useBlocker.tsx +320 -0
  352. package/src/useCanGoBack.ts +5 -0
  353. package/src/useLoaderData.tsx +91 -0
  354. package/src/useLoaderDeps.tsx +69 -0
  355. package/src/useLocation.tsx +52 -0
  356. package/src/useMatch.tsx +123 -0
  357. package/src/useNavigate.tsx +78 -0
  358. package/src/useParams.tsx +107 -0
  359. package/src/useRouteContext.ts +30 -0
  360. package/src/useRouter.tsx +25 -0
  361. package/src/useRouterState.tsx +86 -0
  362. package/src/useSearch.tsx +105 -0
  363. package/src/utils.ts +125 -0
package/src/link.tsx ADDED
@@ -0,0 +1,984 @@
1
+ import * as React from 'react'
2
+ import { flushSync } from 'react-dom'
3
+ import {
4
+ deepEqual,
5
+ exactPathTest,
6
+ functionalUpdate,
7
+ isDangerousProtocol,
8
+ preloadWarning,
9
+ removeTrailingSlash,
10
+ } from '@tanstack/router-core'
11
+ import { isServer } from '@tanstack/router-core/isServer'
12
+ import { useRouterState } from './useRouterState'
13
+ import { useRouter } from './useRouter'
14
+
15
+ import { useForwardedRef, useIntersectionObserver } from './utils'
16
+
17
+ import { useHydrated } from './ClientOnly'
18
+ import type {
19
+ AnyRouter,
20
+ Constrain,
21
+ LinkOptions,
22
+ RegisteredRouter,
23
+ RoutePaths,
24
+ } from '@tanstack/router-core'
25
+ import type { ReactNode } from 'react'
26
+ import type {
27
+ ValidateLinkOptions,
28
+ ValidateLinkOptionsArray,
29
+ } from './typePrimitives'
30
+
31
+ /**
32
+ * Build anchor-like props for declarative navigation and preloading.
33
+ *
34
+ * Returns stable `href`, event handlers and accessibility props derived from
35
+ * router options and active state. Used internally by `Link` and custom links.
36
+ *
37
+ * Options cover `to`, `params`, `search`, `hash`, `state`, `preload`,
38
+ * `activeProps`, `inactiveProps`, and more.
39
+ *
40
+ * @returns React anchor props suitable for `<a>` or custom components.
41
+ * @link https://tanstack.com/router/latest/docs/framework/react/api/router/useLinkPropsHook
42
+ */
43
+ export function useLinkProps<
44
+ TRouter extends AnyRouter = RegisteredRouter,
45
+ const TFrom extends string = string,
46
+ const TTo extends string | undefined = undefined,
47
+ const TMaskFrom extends string = TFrom,
48
+ const TMaskTo extends string = '',
49
+ >(
50
+ options: UseLinkPropsOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,
51
+ forwardedRef?: React.ForwardedRef<Element>,
52
+ ): React.ComponentPropsWithRef<'a'> {
53
+ const router = useRouter()
54
+ const innerRef = useForwardedRef(forwardedRef)
55
+
56
+ // Determine if we're on the server - used for tree-shaking client-only code
57
+ const _isServer = isServer ?? router.isServer
58
+
59
+ const {
60
+ // custom props
61
+ activeProps,
62
+ inactiveProps,
63
+ activeOptions,
64
+ to,
65
+ preload: userPreload,
66
+ preloadDelay: userPreloadDelay,
67
+ hashScrollIntoView,
68
+ replace,
69
+ startTransition,
70
+ resetScroll,
71
+ viewTransition,
72
+ // element props
73
+ children,
74
+ target,
75
+ disabled,
76
+ style,
77
+ className,
78
+ onClick,
79
+ onBlur,
80
+ onFocus,
81
+ onMouseEnter,
82
+ onMouseLeave,
83
+ onTouchStart,
84
+ ignoreBlocker,
85
+ // prevent these from being returned
86
+ params: _params,
87
+ search: _search,
88
+ hash: _hash,
89
+ state: _state,
90
+ mask: _mask,
91
+ reloadDocument: _reloadDocument,
92
+ unsafeRelative: _unsafeRelative,
93
+ from: _from,
94
+ _fromLocation,
95
+ ...propsSafeToSpread
96
+ } = options
97
+
98
+ // ==========================================================================
99
+ // SERVER EARLY RETURN
100
+ // On the server, we return static props without any event handlers,
101
+ // effects, or client-side interactivity.
102
+ //
103
+ // For SSR parity (to avoid hydration errors), we still compute the link's
104
+ // active status on the server, but we avoid creating any router-state
105
+ // subscriptions by reading from `router.state` directly.
106
+ //
107
+ // Note: `location.hash` is not available on the server.
108
+ // ==========================================================================
109
+ if (_isServer) {
110
+ const safeInternal = isSafeInternal(to)
111
+
112
+ // If `to` is obviously an absolute URL, treat as external and avoid
113
+ // computing the internal location via `buildLocation`.
114
+ if (
115
+ typeof to === 'string' &&
116
+ !safeInternal &&
117
+ // Quick checks to avoid `new URL` in common internal-like cases
118
+ to.indexOf(':') > -1
119
+ ) {
120
+ try {
121
+ new URL(to)
122
+ if (isDangerousProtocol(to, router.protocolAllowlist)) {
123
+ if (process.env.NODE_ENV !== 'production') {
124
+ console.warn(`Blocked Link with dangerous protocol: ${to}`)
125
+ }
126
+ return {
127
+ ...propsSafeToSpread,
128
+ ref: innerRef as React.ComponentPropsWithRef<'a'>['ref'],
129
+ href: undefined,
130
+ ...(children && { children }),
131
+ ...(target && { target }),
132
+ ...(disabled && { disabled }),
133
+ ...(style && { style }),
134
+ ...(className && { className }),
135
+ }
136
+ }
137
+
138
+ return {
139
+ ...propsSafeToSpread,
140
+ ref: innerRef as React.ComponentPropsWithRef<'a'>['ref'],
141
+ href: to,
142
+ ...(children && { children }),
143
+ ...(target && { target }),
144
+ ...(disabled && { disabled }),
145
+ ...(style && { style }),
146
+ ...(className && { className }),
147
+ }
148
+ } catch {
149
+ // Not an absolute URL
150
+ }
151
+ }
152
+
153
+ const next = router.buildLocation({ ...options, from: options.from } as any)
154
+
155
+ // Use publicHref - it contains the correct href for display
156
+ // When a rewrite changes the origin, publicHref is the full URL
157
+ // Otherwise it's the origin-stripped path
158
+ // This avoids constructing URL objects in the hot path
159
+ const hrefOptionPublicHref = next.maskedLocation
160
+ ? next.maskedLocation.publicHref
161
+ : next.publicHref
162
+ const hrefOptionExternal = next.maskedLocation
163
+ ? next.maskedLocation.external
164
+ : next.external
165
+ const hrefOption = getHrefOption(
166
+ hrefOptionPublicHref,
167
+ hrefOptionExternal,
168
+ router.history,
169
+ disabled,
170
+ )
171
+
172
+ const externalLink = (() => {
173
+ if (hrefOption?.external) {
174
+ if (isDangerousProtocol(hrefOption.href, router.protocolAllowlist)) {
175
+ if (process.env.NODE_ENV !== 'production') {
176
+ console.warn(
177
+ `Blocked Link with dangerous protocol: ${hrefOption.href}`,
178
+ )
179
+ }
180
+ return undefined
181
+ }
182
+ return hrefOption.href
183
+ }
184
+
185
+ if (safeInternal) return undefined
186
+
187
+ // Only attempt URL parsing when it looks like an absolute URL.
188
+ if (typeof to === 'string' && to.indexOf(':') > -1) {
189
+ try {
190
+ new URL(to)
191
+ if (isDangerousProtocol(to, router.protocolAllowlist)) {
192
+ if (process.env.NODE_ENV !== 'production') {
193
+ console.warn(`Blocked Link with dangerous protocol: ${to}`)
194
+ }
195
+ return undefined
196
+ }
197
+ return to
198
+ } catch {}
199
+ }
200
+
201
+ return undefined
202
+ })()
203
+
204
+ const isActive = (() => {
205
+ if (externalLink) return false
206
+
207
+ const currentLocation = router.state.location
208
+
209
+ const exact = activeOptions?.exact ?? false
210
+
211
+ if (exact) {
212
+ const testExact = exactPathTest(
213
+ currentLocation.pathname,
214
+ next.pathname,
215
+ router.basepath,
216
+ )
217
+ if (!testExact) {
218
+ return false
219
+ }
220
+ } else {
221
+ const currentPathSplit = removeTrailingSlash(
222
+ currentLocation.pathname,
223
+ router.basepath,
224
+ )
225
+ const nextPathSplit = removeTrailingSlash(
226
+ next.pathname,
227
+ router.basepath,
228
+ )
229
+
230
+ const pathIsFuzzyEqual =
231
+ currentPathSplit.startsWith(nextPathSplit) &&
232
+ (currentPathSplit.length === nextPathSplit.length ||
233
+ currentPathSplit[nextPathSplit.length] === '/')
234
+
235
+ if (!pathIsFuzzyEqual) {
236
+ return false
237
+ }
238
+ }
239
+
240
+ const includeSearch = activeOptions?.includeSearch ?? true
241
+ if (includeSearch) {
242
+ if (currentLocation.search !== next.search) {
243
+ const currentSearchEmpty =
244
+ !currentLocation.search ||
245
+ (typeof currentLocation.search === 'object' &&
246
+ Object.keys(currentLocation.search).length === 0)
247
+ const nextSearchEmpty =
248
+ !next.search ||
249
+ (typeof next.search === 'object' &&
250
+ Object.keys(next.search).length === 0)
251
+
252
+ if (!(currentSearchEmpty && nextSearchEmpty)) {
253
+ const searchTest = deepEqual(currentLocation.search, next.search, {
254
+ partial: !exact,
255
+ ignoreUndefined: !activeOptions?.explicitUndefined,
256
+ })
257
+ if (!searchTest) {
258
+ return false
259
+ }
260
+ }
261
+ }
262
+ }
263
+
264
+ // Hash is not available on the server
265
+ if (activeOptions?.includeHash) {
266
+ return false
267
+ }
268
+
269
+ return true
270
+ })()
271
+
272
+ if (externalLink) {
273
+ return {
274
+ ...propsSafeToSpread,
275
+ ref: innerRef as React.ComponentPropsWithRef<'a'>['ref'],
276
+ href: externalLink,
277
+ ...(children && { children }),
278
+ ...(target && { target }),
279
+ ...(disabled && { disabled }),
280
+ ...(style && { style }),
281
+ ...(className && { className }),
282
+ }
283
+ }
284
+
285
+ const resolvedActiveProps: React.HTMLAttributes<HTMLAnchorElement> =
286
+ isActive
287
+ ? (functionalUpdate(activeProps as any, {}) ?? STATIC_ACTIVE_OBJECT)
288
+ : STATIC_EMPTY_OBJECT
289
+
290
+ const resolvedInactiveProps: React.HTMLAttributes<HTMLAnchorElement> =
291
+ isActive
292
+ ? STATIC_EMPTY_OBJECT
293
+ : (functionalUpdate(inactiveProps, {}) ?? STATIC_EMPTY_OBJECT)
294
+
295
+ const resolvedStyle = (() => {
296
+ const baseStyle = style
297
+ const activeStyle = resolvedActiveProps.style
298
+ const inactiveStyle = resolvedInactiveProps.style
299
+
300
+ if (!baseStyle && !activeStyle && !inactiveStyle) {
301
+ return undefined
302
+ }
303
+
304
+ if (baseStyle && !activeStyle && !inactiveStyle) {
305
+ return baseStyle
306
+ }
307
+
308
+ if (!baseStyle && activeStyle && !inactiveStyle) {
309
+ return activeStyle
310
+ }
311
+
312
+ if (!baseStyle && !activeStyle && inactiveStyle) {
313
+ return inactiveStyle
314
+ }
315
+
316
+ return {
317
+ ...baseStyle,
318
+ ...activeStyle,
319
+ ...inactiveStyle,
320
+ }
321
+ })()
322
+
323
+ const resolvedClassName = (() => {
324
+ const baseClassName = className
325
+ const activeClassName = resolvedActiveProps.className
326
+ const inactiveClassName = resolvedInactiveProps.className
327
+
328
+ if (!baseClassName && !activeClassName && !inactiveClassName) {
329
+ return ''
330
+ }
331
+
332
+ let out = ''
333
+
334
+ if (baseClassName) {
335
+ out = baseClassName
336
+ }
337
+
338
+ if (activeClassName) {
339
+ out = out ? `${out} ${activeClassName}` : activeClassName
340
+ }
341
+
342
+ if (inactiveClassName) {
343
+ out = out ? `${out} ${inactiveClassName}` : inactiveClassName
344
+ }
345
+
346
+ return out
347
+ })()
348
+
349
+ return {
350
+ ...propsSafeToSpread,
351
+ ...resolvedActiveProps,
352
+ ...resolvedInactiveProps,
353
+ href: hrefOption?.href,
354
+ ref: innerRef as React.ComponentPropsWithRef<'a'>['ref'],
355
+ disabled: !!disabled,
356
+ target,
357
+ ...(resolvedStyle && { style: resolvedStyle }),
358
+ ...(resolvedClassName && { className: resolvedClassName }),
359
+ ...(disabled && STATIC_DISABLED_PROPS),
360
+ ...(isActive && STATIC_ACTIVE_PROPS),
361
+ }
362
+ }
363
+
364
+ // ==========================================================================
365
+ // CLIENT-ONLY CODE
366
+ // Everything below this point only runs on the client. The `isServer` check
367
+ // above is a compile-time constant that bundlers use for dead code elimination,
368
+ // so this entire section is removed from server bundles.
369
+ //
370
+ // We disable the rules-of-hooks lint rule because these hooks appear after
371
+ // an early return. This is safe because:
372
+ // 1. `isServer` is a compile-time constant from conditional exports
373
+ // 2. In server bundles, this code is completely eliminated by the bundler
374
+ // 3. In client bundles, `isServer` is `false`, so the early return never executes
375
+ // ==========================================================================
376
+
377
+ // eslint-disable-next-line react-hooks/rules-of-hooks
378
+ const isHydrated = useHydrated()
379
+
380
+ // subscribe to path/search/hash/params to re-build location when they change
381
+ // eslint-disable-next-line react-hooks/rules-of-hooks
382
+ const currentLocationState = useRouterState({
383
+ select: (s) => {
384
+ const leaf = s.matches[s.matches.length - 1]
385
+ return {
386
+ search: leaf?.search,
387
+ hash: s.location.hash,
388
+ path: leaf?.pathname, // path + params
389
+ }
390
+ },
391
+ structuralSharing: true as any,
392
+ })
393
+
394
+ const from = options.from
395
+
396
+ // eslint-disable-next-line react-hooks/rules-of-hooks
397
+ const _options = React.useMemo(
398
+ () => {
399
+ return { ...options, from }
400
+ },
401
+ // eslint-disable-next-line react-hooks/exhaustive-deps
402
+ [
403
+ router,
404
+ currentLocationState,
405
+ from,
406
+ options._fromLocation,
407
+ options.hash,
408
+ options.to,
409
+ options.search,
410
+ options.params,
411
+ options.state,
412
+ options.mask,
413
+ options.unsafeRelative,
414
+ ],
415
+ )
416
+
417
+ // eslint-disable-next-line react-hooks/rules-of-hooks
418
+ const next = React.useMemo(
419
+ () => router.buildLocation({ ..._options } as any),
420
+ [router, _options],
421
+ )
422
+
423
+ // Use publicHref - it contains the correct href for display
424
+ // When a rewrite changes the origin, publicHref is the full URL
425
+ // Otherwise it's the origin-stripped path
426
+ // This avoids constructing URL objects in the hot path
427
+ const hrefOptionPublicHref = next.maskedLocation
428
+ ? next.maskedLocation.publicHref
429
+ : next.publicHref
430
+ const hrefOptionExternal = next.maskedLocation
431
+ ? next.maskedLocation.external
432
+ : next.external
433
+ // eslint-disable-next-line react-hooks/rules-of-hooks
434
+ const hrefOption = React.useMemo(
435
+ () =>
436
+ getHrefOption(
437
+ hrefOptionPublicHref,
438
+ hrefOptionExternal,
439
+ router.history,
440
+ disabled,
441
+ ),
442
+ [disabled, hrefOptionExternal, hrefOptionPublicHref, router.history],
443
+ )
444
+
445
+ // eslint-disable-next-line react-hooks/rules-of-hooks
446
+ const externalLink = React.useMemo(() => {
447
+ if (hrefOption?.external) {
448
+ // Block dangerous protocols for external links
449
+ if (isDangerousProtocol(hrefOption.href, router.protocolAllowlist)) {
450
+ if (process.env.NODE_ENV !== 'production') {
451
+ console.warn(
452
+ `Blocked Link with dangerous protocol: ${hrefOption.href}`,
453
+ )
454
+ }
455
+ return undefined
456
+ }
457
+ return hrefOption.href
458
+ }
459
+ const safeInternal = isSafeInternal(to)
460
+ if (safeInternal) return undefined
461
+ if (typeof to !== 'string' || to.indexOf(':') === -1) return undefined
462
+ try {
463
+ new URL(to as any)
464
+ // Block dangerous protocols like javascript:, blob:, data:
465
+ if (isDangerousProtocol(to, router.protocolAllowlist)) {
466
+ if (process.env.NODE_ENV !== 'production') {
467
+ console.warn(`Blocked Link with dangerous protocol: ${to}`)
468
+ }
469
+ return undefined
470
+ }
471
+ return to
472
+ } catch {}
473
+ return undefined
474
+ }, [to, hrefOption, router.protocolAllowlist])
475
+
476
+ // eslint-disable-next-line react-hooks/rules-of-hooks
477
+ const isActive = useRouterState({
478
+ select: (s) => {
479
+ if (externalLink) return false
480
+ if (activeOptions?.exact) {
481
+ const testExact = exactPathTest(
482
+ s.location.pathname,
483
+ next.pathname,
484
+ router.basepath,
485
+ )
486
+ if (!testExact) {
487
+ return false
488
+ }
489
+ } else {
490
+ const currentPathSplit = removeTrailingSlash(
491
+ s.location.pathname,
492
+ router.basepath,
493
+ )
494
+ const nextPathSplit = removeTrailingSlash(
495
+ next.pathname,
496
+ router.basepath,
497
+ )
498
+
499
+ const pathIsFuzzyEqual =
500
+ currentPathSplit.startsWith(nextPathSplit) &&
501
+ (currentPathSplit.length === nextPathSplit.length ||
502
+ currentPathSplit[nextPathSplit.length] === '/')
503
+
504
+ if (!pathIsFuzzyEqual) {
505
+ return false
506
+ }
507
+ }
508
+
509
+ if (activeOptions?.includeSearch ?? true) {
510
+ const searchTest = deepEqual(s.location.search, next.search, {
511
+ partial: !activeOptions?.exact,
512
+ ignoreUndefined: !activeOptions?.explicitUndefined,
513
+ })
514
+ if (!searchTest) {
515
+ return false
516
+ }
517
+ }
518
+
519
+ if (activeOptions?.includeHash) {
520
+ return isHydrated && s.location.hash === next.hash
521
+ }
522
+ return true
523
+ },
524
+ })
525
+
526
+ // Get the active props
527
+ const resolvedActiveProps: React.HTMLAttributes<HTMLAnchorElement> = isActive
528
+ ? (functionalUpdate(activeProps as any, {}) ?? STATIC_ACTIVE_OBJECT)
529
+ : STATIC_EMPTY_OBJECT
530
+
531
+ // Get the inactive props
532
+ const resolvedInactiveProps: React.HTMLAttributes<HTMLAnchorElement> =
533
+ isActive
534
+ ? STATIC_EMPTY_OBJECT
535
+ : (functionalUpdate(inactiveProps, {}) ?? STATIC_EMPTY_OBJECT)
536
+
537
+ const resolvedClassName = [
538
+ className,
539
+ resolvedActiveProps.className,
540
+ resolvedInactiveProps.className,
541
+ ]
542
+ .filter(Boolean)
543
+ .join(' ')
544
+
545
+ const resolvedStyle = (style ||
546
+ resolvedActiveProps.style ||
547
+ resolvedInactiveProps.style) && {
548
+ ...style,
549
+ ...resolvedActiveProps.style,
550
+ ...resolvedInactiveProps.style,
551
+ }
552
+
553
+ // eslint-disable-next-line react-hooks/rules-of-hooks
554
+ const [isTransitioning, setIsTransitioning] = React.useState(false)
555
+ // eslint-disable-next-line react-hooks/rules-of-hooks
556
+ const hasRenderFetched = React.useRef(false)
557
+
558
+ const preload =
559
+ options.reloadDocument || externalLink
560
+ ? false
561
+ : (userPreload ?? router.options.defaultPreload)
562
+ const preloadDelay =
563
+ userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0
564
+
565
+ // eslint-disable-next-line react-hooks/rules-of-hooks
566
+ const doPreload = React.useCallback(() => {
567
+ router
568
+ .preloadRoute({ ..._options, _builtLocation: next } as any)
569
+ .catch((err) => {
570
+ console.warn(err)
571
+ console.warn(preloadWarning)
572
+ })
573
+ }, [router, _options, next])
574
+
575
+ // eslint-disable-next-line react-hooks/rules-of-hooks
576
+ const preloadViewportIoCallback = React.useCallback(
577
+ (entry: IntersectionObserverEntry | undefined) => {
578
+ if (entry?.isIntersecting) {
579
+ doPreload()
580
+ }
581
+ },
582
+ [doPreload],
583
+ )
584
+
585
+ // eslint-disable-next-line react-hooks/rules-of-hooks
586
+ useIntersectionObserver(
587
+ innerRef,
588
+ preloadViewportIoCallback,
589
+ intersectionObserverOptions,
590
+ { disabled: !!disabled || !(preload === 'viewport') },
591
+ )
592
+
593
+ // eslint-disable-next-line react-hooks/rules-of-hooks
594
+ React.useEffect(() => {
595
+ if (hasRenderFetched.current) {
596
+ return
597
+ }
598
+ if (!disabled && preload === 'render') {
599
+ doPreload()
600
+ hasRenderFetched.current = true
601
+ }
602
+ }, [disabled, doPreload, preload])
603
+
604
+ // The click handler
605
+ const handleClick = (e: React.MouseEvent) => {
606
+ // Check actual element's target attribute as fallback
607
+ const elementTarget = (
608
+ e.currentTarget as HTMLAnchorElement | SVGAElement
609
+ ).getAttribute('target')
610
+ const effectiveTarget = target !== undefined ? target : elementTarget
611
+
612
+ if (
613
+ !disabled &&
614
+ !isCtrlEvent(e) &&
615
+ !e.defaultPrevented &&
616
+ (!effectiveTarget || effectiveTarget === '_self') &&
617
+ e.button === 0
618
+ ) {
619
+ e.preventDefault()
620
+
621
+ flushSync(() => {
622
+ setIsTransitioning(true)
623
+ })
624
+
625
+ const unsub = router.subscribe('onResolved', () => {
626
+ unsub()
627
+ setIsTransitioning(false)
628
+ })
629
+
630
+ // All is well? Navigate!
631
+ // N.B. we don't call `router.commitLocation(next) here because we want to run `validateSearch` before committing
632
+ router.navigate({
633
+ ..._options,
634
+ replace,
635
+ resetScroll,
636
+ hashScrollIntoView,
637
+ startTransition,
638
+ viewTransition,
639
+ ignoreBlocker,
640
+ })
641
+ }
642
+ }
643
+
644
+ if (externalLink) {
645
+ return {
646
+ ...propsSafeToSpread,
647
+ ref: innerRef as React.ComponentPropsWithRef<'a'>['ref'],
648
+ href: externalLink,
649
+ ...(children && { children }),
650
+ ...(target && { target }),
651
+ ...(disabled && { disabled }),
652
+ ...(style && { style }),
653
+ ...(className && { className }),
654
+ ...(onClick && { onClick }),
655
+ ...(onBlur && { onBlur }),
656
+ ...(onFocus && { onFocus }),
657
+ ...(onMouseEnter && { onMouseEnter }),
658
+ ...(onMouseLeave && { onMouseLeave }),
659
+ ...(onTouchStart && { onTouchStart }),
660
+ }
661
+ }
662
+
663
+ const enqueueIntentPreload = (e: React.MouseEvent | React.FocusEvent) => {
664
+ if (disabled || preload !== 'intent') return
665
+
666
+ if (!preloadDelay) {
667
+ doPreload()
668
+ return
669
+ }
670
+
671
+ const eventTarget = e.currentTarget
672
+
673
+ if (timeoutMap.has(eventTarget)) {
674
+ return
675
+ }
676
+
677
+ const id = setTimeout(() => {
678
+ timeoutMap.delete(eventTarget)
679
+ doPreload()
680
+ }, preloadDelay)
681
+ timeoutMap.set(eventTarget, id)
682
+ }
683
+
684
+ const handleTouchStart = (_: React.TouchEvent) => {
685
+ if (disabled || preload !== 'intent') return
686
+ doPreload()
687
+ }
688
+
689
+ const handleLeave = (e: React.MouseEvent | React.FocusEvent) => {
690
+ if (disabled || !preload || !preloadDelay) return
691
+ const eventTarget = e.currentTarget
692
+ const id = timeoutMap.get(eventTarget)
693
+ if (id) {
694
+ clearTimeout(id)
695
+ timeoutMap.delete(eventTarget)
696
+ }
697
+ }
698
+
699
+ return {
700
+ ...propsSafeToSpread,
701
+ ...resolvedActiveProps,
702
+ ...resolvedInactiveProps,
703
+ href: hrefOption?.href,
704
+ ref: innerRef as React.ComponentPropsWithRef<'a'>['ref'],
705
+ onClick: composeHandlers([onClick, handleClick]),
706
+ onBlur: composeHandlers([onBlur, handleLeave]),
707
+ onFocus: composeHandlers([onFocus, enqueueIntentPreload]),
708
+ onMouseEnter: composeHandlers([onMouseEnter, enqueueIntentPreload]),
709
+ onMouseLeave: composeHandlers([onMouseLeave, handleLeave]),
710
+ onTouchStart: composeHandlers([onTouchStart, handleTouchStart]),
711
+ disabled: !!disabled,
712
+ target,
713
+ ...(resolvedStyle && { style: resolvedStyle }),
714
+ ...(resolvedClassName && { className: resolvedClassName }),
715
+ ...(disabled && STATIC_DISABLED_PROPS),
716
+ ...(isActive && STATIC_ACTIVE_PROPS),
717
+ ...(isHydrated && isTransitioning && STATIC_TRANSITIONING_PROPS),
718
+ }
719
+ }
720
+
721
+ const STATIC_EMPTY_OBJECT = {}
722
+ const STATIC_ACTIVE_OBJECT = { className: 'active' }
723
+ const STATIC_DISABLED_PROPS = { role: 'link', 'aria-disabled': true }
724
+ const STATIC_ACTIVE_PROPS = { 'data-status': 'active', 'aria-current': 'page' }
725
+ const STATIC_TRANSITIONING_PROPS = { 'data-transitioning': 'transitioning' }
726
+
727
+ const timeoutMap = new WeakMap<EventTarget, ReturnType<typeof setTimeout>>()
728
+
729
+ const intersectionObserverOptions: IntersectionObserverInit = {
730
+ rootMargin: '100px',
731
+ }
732
+
733
+ const composeHandlers =
734
+ (handlers: Array<undefined | React.EventHandler<any>>) =>
735
+ (e: React.SyntheticEvent) => {
736
+ for (const handler of handlers) {
737
+ if (!handler) continue
738
+ if (e.defaultPrevented) return
739
+ handler(e)
740
+ }
741
+ }
742
+
743
+ function getHrefOption(
744
+ publicHref: string,
745
+ external: boolean,
746
+ history: AnyRouter['history'],
747
+ disabled: boolean | undefined,
748
+ ) {
749
+ if (disabled) return undefined
750
+ // Full URL means rewrite changed the origin - treat as external-like
751
+ if (external) {
752
+ return { href: publicHref, external: true }
753
+ }
754
+ return {
755
+ href: history.createHref(publicHref) || '/',
756
+ external: false,
757
+ }
758
+ }
759
+
760
+ function isSafeInternal(to: unknown) {
761
+ if (typeof to !== 'string') return false
762
+ const zero = to.charCodeAt(0)
763
+ if (zero === 47) return to.charCodeAt(1) !== 47 // '/' but not '//'
764
+ return zero === 46 // '.', '..', './', '../'
765
+ }
766
+
767
+ type UseLinkReactProps<TComp> = TComp extends keyof React.JSX.IntrinsicElements
768
+ ? React.JSX.IntrinsicElements[TComp]
769
+ : TComp extends React.ComponentType<any>
770
+ ? React.ComponentPropsWithoutRef<TComp> &
771
+ React.RefAttributes<React.ComponentRef<TComp>>
772
+ : never
773
+
774
+ export type UseLinkPropsOptions<
775
+ TRouter extends AnyRouter = RegisteredRouter,
776
+ TFrom extends RoutePaths<TRouter['routeTree']> | string = string,
777
+ TTo extends string | undefined = '.',
778
+ TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,
779
+ TMaskTo extends string = '.',
780
+ > = ActiveLinkOptions<'a', TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &
781
+ UseLinkReactProps<'a'>
782
+
783
+ export type ActiveLinkOptions<
784
+ TComp = 'a',
785
+ TRouter extends AnyRouter = RegisteredRouter,
786
+ TFrom extends string = string,
787
+ TTo extends string | undefined = '.',
788
+ TMaskFrom extends string = TFrom,
789
+ TMaskTo extends string = '.',
790
+ > = LinkOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &
791
+ ActiveLinkOptionProps<TComp>
792
+
793
+ type ActiveLinkProps<TComp> = Partial<
794
+ LinkComponentReactProps<TComp> & {
795
+ [key: `data-${string}`]: unknown
796
+ }
797
+ >
798
+
799
+ export interface ActiveLinkOptionProps<TComp = 'a'> {
800
+ /**
801
+ * A function that returns additional props for the `active` state of this link.
802
+ * These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
803
+ */
804
+ activeProps?: ActiveLinkProps<TComp> | (() => ActiveLinkProps<TComp>)
805
+ /**
806
+ * A function that returns additional props for the `inactive` state of this link.
807
+ * These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
808
+ */
809
+ inactiveProps?: ActiveLinkProps<TComp> | (() => ActiveLinkProps<TComp>)
810
+ }
811
+
812
+ export type LinkProps<
813
+ TComp = 'a',
814
+ TRouter extends AnyRouter = RegisteredRouter,
815
+ TFrom extends string = string,
816
+ TTo extends string | undefined = '.',
817
+ TMaskFrom extends string = TFrom,
818
+ TMaskTo extends string = '.',
819
+ > = ActiveLinkOptions<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &
820
+ LinkPropsChildren
821
+
822
+ export interface LinkPropsChildren {
823
+ // 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
824
+ children?:
825
+ | React.ReactNode
826
+ | ((state: {
827
+ isActive: boolean
828
+ isTransitioning: boolean
829
+ }) => React.ReactNode)
830
+ }
831
+
832
+ type LinkComponentReactProps<TComp> = Omit<
833
+ UseLinkReactProps<TComp>,
834
+ keyof CreateLinkProps
835
+ >
836
+
837
+ export type LinkComponentProps<
838
+ TComp = 'a',
839
+ TRouter extends AnyRouter = RegisteredRouter,
840
+ TFrom extends string = string,
841
+ TTo extends string | undefined = '.',
842
+ TMaskFrom extends string = TFrom,
843
+ TMaskTo extends string = '.',
844
+ > = LinkComponentReactProps<TComp> &
845
+ LinkProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>
846
+
847
+ export type CreateLinkProps = LinkProps<
848
+ any,
849
+ any,
850
+ string,
851
+ string,
852
+ string,
853
+ string
854
+ >
855
+
856
+ export type LinkComponent<
857
+ in out TComp,
858
+ in out TDefaultFrom extends string = string,
859
+ > = <
860
+ TRouter extends AnyRouter = RegisteredRouter,
861
+ const TFrom extends string = TDefaultFrom,
862
+ const TTo extends string | undefined = undefined,
863
+ const TMaskFrom extends string = TFrom,
864
+ const TMaskTo extends string = '',
865
+ >(
866
+ props: LinkComponentProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,
867
+ ) => React.ReactElement
868
+
869
+ export interface LinkComponentRoute<
870
+ in out TDefaultFrom extends string = string,
871
+ > {
872
+ defaultFrom: TDefaultFrom;
873
+ <
874
+ TRouter extends AnyRouter = RegisteredRouter,
875
+ const TTo extends string | undefined = undefined,
876
+ const TMaskTo extends string = '',
877
+ >(
878
+ props: LinkComponentProps<
879
+ 'a',
880
+ TRouter,
881
+ this['defaultFrom'],
882
+ TTo,
883
+ this['defaultFrom'],
884
+ TMaskTo
885
+ >,
886
+ ): React.ReactElement
887
+ }
888
+
889
+ /**
890
+ * Creates a typed Link-like component that preserves TanStack Router's
891
+ * navigation semantics and type-safety while delegating rendering to the
892
+ * provided host component.
893
+ *
894
+ * Useful for integrating design system anchors/buttons while keeping
895
+ * router-aware props (eg. `to`, `params`, `search`, `preload`).
896
+ *
897
+ * @param Comp The host component to render (eg. a design-system Link/Button)
898
+ * @returns A router-aware component with the same API as `Link`.
899
+ * @link https://tanstack.com/router/latest/docs/framework/react/guide/custom-link
900
+ */
901
+ export function createLink<const TComp>(
902
+ Comp: Constrain<TComp, any, (props: CreateLinkProps) => ReactNode>,
903
+ ): LinkComponent<TComp> {
904
+ return React.forwardRef(function CreatedLink(props, ref) {
905
+ return <Link {...(props as any)} _asChild={Comp} ref={ref} />
906
+ }) as any
907
+ }
908
+
909
+ /**
910
+ * A strongly-typed anchor component for declarative navigation.
911
+ * Handles path, search, hash and state updates with optional route preloading
912
+ * and active-state styling.
913
+ *
914
+ * Props:
915
+ * - `preload`: Controls route preloading (eg. 'intent', 'render', 'viewport', true/false)
916
+ * - `preloadDelay`: Delay in ms before preloading on hover
917
+ * - `activeProps`/`inactiveProps`: Additional props merged when link is active/inactive
918
+ * - `resetScroll`/`hashScrollIntoView`: Control scroll behavior on navigation
919
+ * - `viewTransition`/`startTransition`: Use View Transitions/React transitions for navigation
920
+ * - `ignoreBlocker`: Bypass registered blockers
921
+ *
922
+ * @returns An anchor-like element that navigates without full page reloads.
923
+ * @link https://tanstack.com/router/latest/docs/framework/react/api/router/linkComponent
924
+ */
925
+ export const Link: LinkComponent<'a'> = React.forwardRef<Element, any>(
926
+ (props, ref) => {
927
+ const { _asChild, ...rest } = props
928
+ const { type: _type, ...linkProps } = useLinkProps(rest as any, ref)
929
+
930
+ const children =
931
+ typeof rest.children === 'function'
932
+ ? rest.children({
933
+ isActive: (linkProps as any)['data-status'] === 'active',
934
+ })
935
+ : rest.children
936
+
937
+ if (!_asChild) {
938
+ // the ReturnType of useLinkProps returns the correct type for a <a> element, not a general component that has a disabled prop
939
+ // @ts-expect-error
940
+ const { disabled: _, ...rest } = linkProps
941
+ return React.createElement('a', rest, children)
942
+ }
943
+ return React.createElement(_asChild, linkProps, children)
944
+ },
945
+ ) as any
946
+
947
+ function isCtrlEvent(e: React.MouseEvent) {
948
+ return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
949
+ }
950
+
951
+ export type LinkOptionsFnOptions<
952
+ TOptions,
953
+ TComp,
954
+ TRouter extends AnyRouter = RegisteredRouter,
955
+ > =
956
+ TOptions extends ReadonlyArray<any>
957
+ ? ValidateLinkOptionsArray<TRouter, TOptions, string, TComp>
958
+ : ValidateLinkOptions<TRouter, TOptions, string, TComp>
959
+
960
+ export type LinkOptionsFn<TComp> = <
961
+ const TOptions,
962
+ TRouter extends AnyRouter = RegisteredRouter,
963
+ >(
964
+ options: LinkOptionsFnOptions<TOptions, TComp, TRouter>,
965
+ ) => TOptions
966
+
967
+ /**
968
+ * Validate and reuse navigation options for `Link`, `navigate` or `redirect`.
969
+ * Accepts a literal options object and returns it typed for later spreading.
970
+ * @example
971
+ * const opts = linkOptions({ to: '/dashboard', search: { tab: 'home' } })
972
+ * @link https://tanstack.com/router/latest/docs/framework/react/api/router/linkOptions
973
+ */
974
+ export const linkOptions: LinkOptionsFn<'a'> = (options) => {
975
+ return options as any
976
+ }
977
+
978
+ /**
979
+ * Type-check a literal object for use with `Link`, `navigate` or `redirect`.
980
+ * Use to validate and reuse navigation options across your app.
981
+ * @example
982
+ * const opts = linkOptions({ to: '/dashboard', search: { tab: 'home' } })
983
+ * @link https://tanstack.com/router/latest/docs/framework/react/api/router/linkOptions
984
+ */