@tanstack/react-router 1.115.2 → 1.116.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.
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const React = require("react");
5
+ function ClientOnly({ children, fallback = null }) {
6
+ return useHydrated() ? /* @__PURE__ */ jsxRuntime.jsx(React.Fragment, { children }) : /* @__PURE__ */ jsxRuntime.jsx(React.Fragment, { children: fallback });
7
+ }
8
+ function useHydrated() {
9
+ return React.useSyncExternalStore(
10
+ subscribe,
11
+ () => true,
12
+ () => false
13
+ );
14
+ }
15
+ function subscribe() {
16
+ return () => {
17
+ };
18
+ }
19
+ exports.ClientOnly = ClientOnly;
20
+ //# sourceMappingURL=ClientOnly.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClientOnly.cjs","sources":["../../src/ClientOnly.tsx"],"sourcesContent":["import React from 'react'\n\nexport interface ClientOnlyProps {\n /**\n * The children to render if the JS is loaded.\n */\n children: React.ReactNode\n /**\n * The fallback component to render if the JS is not yet loaded.\n */\n fallback?: React.ReactNode\n}\n\n/**\n * Render the children only after the JS has loaded client-side. Use an optional\n * fallback component if the JS is not yet loaded.\n *\n * @example\n * Render a Chart component if JS loads, renders a simple FakeChart\n * component server-side or if there is no JS. The FakeChart can have only the\n * UI without the behavior or be a loading spinner or skeleton.\n *\n * ```tsx\n * return (\n * <ClientOnly fallback={<FakeChart />}>\n * <Chart />\n * </ClientOnly>\n * )\n * ```\n */\nexport function ClientOnly({ children, fallback = null }: ClientOnlyProps) {\n return useHydrated() ? (\n <React.Fragment>{children}</React.Fragment>\n ) : (\n <React.Fragment>{fallback}</React.Fragment>\n )\n}\n\n/**\n * Return a boolean indicating if the JS has been hydrated already.\n * When doing Server-Side Rendering, the result will always be false.\n * When doing Client-Side Rendering, the result will always be false on the\n * first render and true from then on. Even if a new component renders it will\n * always start with true.\n *\n * @example\n * ```tsx\n * // Disable a button that needs JS to work.\n * let hydrated = useHydrated()\n * return (\n * <button type=\"button\" disabled={!hydrated} onClick={doSomethingCustom}>\n * Click me\n * </button>\n * )\n * ```\n * @returns True if the JS has been hydrated already, false otherwise.\n */\nfunction useHydrated(): boolean {\n return React.useSyncExternalStore(\n subscribe,\n () => true,\n () => false,\n )\n}\n\nfunction subscribe() {\n return () => {}\n}\n"],"names":["jsx"],"mappings":";;;;AA8BO,SAAS,WAAW,EAAE,UAAU,WAAW,QAAyB;AACzE,SAAO,YAAY,IAChBA,2BAAAA,IAAA,MAAM,UAAN,EAAgB,SAAS,CAAA,IAEzBA,2BAAAA,IAAA,MAAM,UAAN,EAAgB,UAAS,SAAA,CAAA;AAE9B;AAqBA,SAAS,cAAuB;AAC9B,SAAO,MAAM;AAAA,IACX;AAAA,IACA,MAAM;AAAA,IACN,MAAM;AAAA,EACR;AACF;AAEA,SAAS,YAAY;AACnB,SAAO,MAAM;AAAA,EAAC;AAChB;;"}
@@ -0,0 +1,29 @@
1
+ import { default as React } from 'react';
2
+ export interface ClientOnlyProps {
3
+ /**
4
+ * The children to render if the JS is loaded.
5
+ */
6
+ children: React.ReactNode;
7
+ /**
8
+ * The fallback component to render if the JS is not yet loaded.
9
+ */
10
+ fallback?: React.ReactNode;
11
+ }
12
+ /**
13
+ * Render the children only after the JS has loaded client-side. Use an optional
14
+ * fallback component if the JS is not yet loaded.
15
+ *
16
+ * @example
17
+ * Render a Chart component if JS loads, renders a simple FakeChart
18
+ * component server-side or if there is no JS. The FakeChart can have only the
19
+ * UI without the behavior or be a loading spinner or skeleton.
20
+ *
21
+ * ```tsx
22
+ * return (
23
+ * <ClientOnly fallback={<FakeChart />}>
24
+ * <Chart />
25
+ * </ClientOnly>
26
+ * )
27
+ * ```
28
+ */
29
+ export declare function ClientOnly({ children, fallback }: ClientOnlyProps): import("react/jsx-runtime").JSX.Element;
@@ -6,6 +6,7 @@ const routerCore = require("@tanstack/router-core");
6
6
  const history = require("@tanstack/history");
7
7
  const awaited = require("./awaited.cjs");
8
8
  const CatchBoundary = require("./CatchBoundary.cjs");
9
+ const ClientOnly = require("./ClientOnly.cjs");
9
10
  const fileRoute = require("./fileRoute.cjs");
10
11
  const lazyRouteComponent = require("./lazyRouteComponent.cjs");
11
12
  const link = require("./link.cjs");
@@ -225,6 +226,7 @@ exports.Await = awaited.Await;
225
226
  exports.useAwaited = awaited.useAwaited;
226
227
  exports.CatchBoundary = CatchBoundary.CatchBoundary;
227
228
  exports.ErrorComponent = CatchBoundary.ErrorComponent;
229
+ exports.ClientOnly = ClientOnly.ClientOnly;
228
230
  exports.FileRoute = fileRoute.FileRoute;
229
231
  exports.FileRouteLoader = fileRoute.FileRouteLoader;
230
232
  exports.LazyRoute = fileRoute.LazyRoute;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -9,6 +9,7 @@ export type { BlockerFn, HistoryLocation, RouterHistory, ParsedPath, HistoryStat
9
9
  export { useAwaited, Await } from './awaited.cjs';
10
10
  export type { AwaitOptions } from './awaited.cjs';
11
11
  export { CatchBoundary, ErrorComponent } from './CatchBoundary.cjs';
12
+ export { ClientOnly } from './ClientOnly.cjs';
12
13
  export { FileRoute, createFileRoute, FileRouteLoader, LazyRoute, createLazyRoute, createLazyFileRoute, } from './fileRoute.cjs';
13
14
  export * from './history.cjs';
14
15
  export { lazyRouteComponent } from './lazyRouteComponent.cjs';
@@ -3,6 +3,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
4
  const React = require("react");
5
5
  const Match = require("./Match.cjs");
6
+ const ClientOnly = require("./ClientOnly.cjs");
6
7
  function _interopNamespaceDefault(e) {
7
8
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
8
9
  if (e) {
@@ -24,23 +25,6 @@ function isModuleNotFoundError(error) {
24
25
  if (typeof (error == null ? void 0 : error.message) !== "string") return false;
25
26
  return error.message.startsWith("Failed to fetch dynamically imported module") || error.message.startsWith("error loading dynamically imported module") || error.message.startsWith("Importing a module script failed");
26
27
  }
27
- function ClientOnly({
28
- children,
29
- fallback = null
30
- }) {
31
- return useHydrated() ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children }) : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback });
32
- }
33
- function subscribe() {
34
- return () => {
35
- };
36
- }
37
- function useHydrated() {
38
- return React__namespace.useSyncExternalStore(
39
- subscribe,
40
- () => true,
41
- () => false
42
- );
43
- }
44
28
  function lazyRouteComponent(importer, exportName, ssr) {
45
29
  let loadPromise;
46
30
  let comp;
@@ -83,14 +67,12 @@ function lazyRouteComponent(importer, exportName, ssr) {
83
67
  throw load();
84
68
  }
85
69
  if ((ssr == null ? void 0 : ssr()) === false) {
86
- return /* @__PURE__ */ jsxRuntime.jsx(ClientOnly, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Match.Outlet, {}), children: React__namespace.createElement(comp, props) });
70
+ return /* @__PURE__ */ jsxRuntime.jsx(ClientOnly.ClientOnly, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Match.Outlet, {}), children: React__namespace.createElement(comp, props) });
87
71
  }
88
72
  return React__namespace.createElement(comp, props);
89
73
  };
90
74
  lazyComp.preload = load;
91
75
  return lazyComp;
92
76
  }
93
- exports.ClientOnly = ClientOnly;
94
77
  exports.lazyRouteComponent = lazyRouteComponent;
95
- exports.useHydrated = useHydrated;
96
78
  //# sourceMappingURL=lazyRouteComponent.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"lazyRouteComponent.cjs","sources":["../../src/lazyRouteComponent.tsx"],"sourcesContent":["import * as React from 'react'\nimport { Outlet } from './Match'\nimport type { AsyncRouteComponent } from './route'\n\n// If the load fails due to module not found, it may mean a new version of\n// the build was deployed and the user's browser is still using an old version.\n// If this happens, the old version in the user's browser would have an outdated\n// URL to the lazy module.\n// In that case, we want to attempt one window refresh to get the latest.\nfunction isModuleNotFoundError(error: any): boolean {\n // chrome: \"Failed to fetch dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // firefox: \"error loading dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // safari: \"Importing a module script failed.\"\n if (typeof error?.message !== 'string') return false\n return (\n error.message.startsWith('Failed to fetch dynamically imported module') ||\n error.message.startsWith('error loading dynamically imported module') ||\n error.message.startsWith('Importing a module script failed')\n )\n}\n\nexport function ClientOnly({\n children,\n fallback = null,\n}: React.PropsWithChildren<{ fallback?: React.ReactNode }>) {\n return useHydrated() ? <>{children}</> : <>{fallback}</>\n}\n\nfunction subscribe() {\n return () => {}\n}\n\nexport function useHydrated() {\n return React.useSyncExternalStore(\n subscribe,\n () => true,\n () => false,\n )\n}\n\nexport function lazyRouteComponent<\n T extends Record<string, any>,\n TKey extends keyof T = 'default',\n>(\n importer: () => Promise<T>,\n exportName?: TKey,\n ssr?: () => boolean,\n): T[TKey] extends (props: infer TProps) => any\n ? AsyncRouteComponent<TProps>\n : never {\n let loadPromise: Promise<any> | undefined\n let comp: T[TKey] | T['default']\n let error: any\n let reload: boolean\n\n const load = () => {\n if (typeof document === 'undefined' && ssr?.() === false) {\n comp = (() => null) as any\n return Promise.resolve()\n }\n if (!loadPromise) {\n loadPromise = importer()\n .then((res) => {\n loadPromise = undefined\n comp = res[exportName ?? 'default']\n })\n .catch((err) => {\n // We don't want an error thrown from preload in this case, because\n // there's nothing we want to do about module not found during preload.\n // Record the error, the rest is handled during the render path.\n error = err\n if (isModuleNotFoundError(error)) {\n if (\n error instanceof Error &&\n typeof window !== 'undefined' &&\n typeof sessionStorage !== 'undefined'\n ) {\n // Again, we want to reload one time on module not found error and not enter\n // a reload loop if there is some other issue besides an old deploy.\n // That's why we store our reload attempt in sessionStorage.\n // Use error.message as key because it contains the module path that failed.\n const storageKey = `tanstack_router_reload:${error.message}`\n if (!sessionStorage.getItem(storageKey)) {\n sessionStorage.setItem(storageKey, '1')\n reload = true\n }\n }\n }\n })\n }\n\n return loadPromise\n }\n\n const lazyComp = function Lazy(props: any) {\n // Now that we're out of preload and into actual render path,\n if (reload) {\n // If it was a module loading error,\n // throw eternal suspense while we wait for window to reload\n window.location.reload()\n throw new Promise(() => {})\n }\n if (error) {\n // Otherwise, just throw the error\n throw error\n }\n\n if (!comp) {\n throw load()\n }\n\n if (ssr?.() === false) {\n return (\n <ClientOnly fallback={<Outlet />}>\n {React.createElement(comp, props)}\n </ClientOnly>\n )\n }\n return React.createElement(comp, props)\n }\n\n ;(lazyComp as any).preload = load\n\n return lazyComp as any\n}\n"],"names":["jsx","Fragment","React","Outlet"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AASA,SAAS,sBAAsB,OAAqB;AAIlD,MAAI,QAAO,+BAAO,aAAY,SAAiB,QAAA;AAC/C,SACE,MAAM,QAAQ,WAAW,6CAA6C,KACtE,MAAM,QAAQ,WAAW,2CAA2C,KACpE,MAAM,QAAQ,WAAW,kCAAkC;AAE/D;AAEO,SAAS,WAAW;AAAA,EACzB;AAAA,EACA,WAAW;AACb,GAA4D;AAC1D,SAAO,YAAgB,IAAAA,+BAAAC,WAAAA,UAAA,EAAG,SAAS,CAAA,0DAAS,UAAS,SAAA,CAAA;AACvD;AAEA,SAAS,YAAY;AACnB,SAAO,MAAM;AAAA,EAAC;AAChB;AAEO,SAAS,cAAc;AAC5B,SAAOC,iBAAM;AAAA,IACX;AAAA,IACA,MAAM;AAAA,IACN,MAAM;AAAA,EACR;AACF;AAEgB,SAAA,mBAId,UACA,YACA,KAGQ;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AAEJ,QAAM,OAAO,MAAM;AACjB,QAAI,OAAO,aAAa,gBAAe,kCAAY,OAAO;AACxD,aAAQ,MAAM;AACd,aAAO,QAAQ,QAAQ;AAAA,IAAA;AAEzB,QAAI,CAAC,aAAa;AAChB,oBAAc,SAAS,EACpB,KAAK,CAAC,QAAQ;AACC,sBAAA;AACP,eAAA,IAAI,cAAc,SAAS;AAAA,MAAA,CACnC,EACA,MAAM,CAAC,QAAQ;AAIN,gBAAA;AACJ,YAAA,sBAAsB,KAAK,GAAG;AAChC,cACE,iBAAiB,SACjB,OAAO,WAAW,eAClB,OAAO,mBAAmB,aAC1B;AAKM,kBAAA,aAAa,0BAA0B,MAAM,OAAO;AAC1D,gBAAI,CAAC,eAAe,QAAQ,UAAU,GAAG;AACxB,6BAAA,QAAQ,YAAY,GAAG;AAC7B,uBAAA;AAAA,YAAA;AAAA,UACX;AAAA,QACF;AAAA,MACF,CACD;AAAA,IAAA;AAGE,WAAA;AAAA,EACT;AAEM,QAAA,WAAW,SAAS,KAAK,OAAY;AAEzC,QAAI,QAAQ;AAGV,aAAO,SAAS,OAAO;AACjB,YAAA,IAAI,QAAQ,MAAM;AAAA,MAAA,CAAE;AAAA,IAAA;AAE5B,QAAI,OAAO;AAEH,YAAA;AAAA,IAAA;AAGR,QAAI,CAAC,MAAM;AACT,YAAM,KAAK;AAAA,IAAA;AAGT,SAAA,kCAAY,OAAO;AAEnB,aAAAF,2BAAA,IAAC,YAAW,EAAA,UAAWA,2BAAAA,IAAAG,MAAAA,QAAA,CAAA,CAAO,GAC3B,UAAMD,iBAAA,cAAc,MAAM,KAAK,EAClC,CAAA;AAAA,IAAA;AAGG,WAAAA,iBAAM,cAAc,MAAM,KAAK;AAAA,EACxC;AAEE,WAAiB,UAAU;AAEtB,SAAA;AACT;;;;"}
1
+ {"version":3,"file":"lazyRouteComponent.cjs","sources":["../../src/lazyRouteComponent.tsx"],"sourcesContent":["import * as React from 'react'\nimport { Outlet } from './Match'\nimport { ClientOnly } from './ClientOnly'\nimport type { AsyncRouteComponent } from './route'\n\n// If the load fails due to module not found, it may mean a new version of\n// the build was deployed and the user's browser is still using an old version.\n// If this happens, the old version in the user's browser would have an outdated\n// URL to the lazy module.\n// In that case, we want to attempt one window refresh to get the latest.\nfunction isModuleNotFoundError(error: any): boolean {\n // chrome: \"Failed to fetch dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // firefox: \"error loading dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // safari: \"Importing a module script failed.\"\n if (typeof error?.message !== 'string') return false\n return (\n error.message.startsWith('Failed to fetch dynamically imported module') ||\n error.message.startsWith('error loading dynamically imported module') ||\n error.message.startsWith('Importing a module script failed')\n )\n}\n\nexport function lazyRouteComponent<\n T extends Record<string, any>,\n TKey extends keyof T = 'default',\n>(\n importer: () => Promise<T>,\n exportName?: TKey,\n ssr?: () => boolean,\n): T[TKey] extends (props: infer TProps) => any\n ? AsyncRouteComponent<TProps>\n : never {\n let loadPromise: Promise<any> | undefined\n let comp: T[TKey] | T['default']\n let error: any\n let reload: boolean\n\n const load = () => {\n if (typeof document === 'undefined' && ssr?.() === false) {\n comp = (() => null) as any\n return Promise.resolve()\n }\n if (!loadPromise) {\n loadPromise = importer()\n .then((res) => {\n loadPromise = undefined\n comp = res[exportName ?? 'default']\n })\n .catch((err) => {\n // We don't want an error thrown from preload in this case, because\n // there's nothing we want to do about module not found during preload.\n // Record the error, the rest is handled during the render path.\n error = err\n if (isModuleNotFoundError(error)) {\n if (\n error instanceof Error &&\n typeof window !== 'undefined' &&\n typeof sessionStorage !== 'undefined'\n ) {\n // Again, we want to reload one time on module not found error and not enter\n // a reload loop if there is some other issue besides an old deploy.\n // That's why we store our reload attempt in sessionStorage.\n // Use error.message as key because it contains the module path that failed.\n const storageKey = `tanstack_router_reload:${error.message}`\n if (!sessionStorage.getItem(storageKey)) {\n sessionStorage.setItem(storageKey, '1')\n reload = true\n }\n }\n }\n })\n }\n\n return loadPromise\n }\n\n const lazyComp = function Lazy(props: any) {\n // Now that we're out of preload and into actual render path,\n if (reload) {\n // If it was a module loading error,\n // throw eternal suspense while we wait for window to reload\n window.location.reload()\n throw new Promise(() => {})\n }\n if (error) {\n // Otherwise, just throw the error\n throw error\n }\n\n if (!comp) {\n throw load()\n }\n\n if (ssr?.() === false) {\n return (\n <ClientOnly fallback={<Outlet />}>\n {React.createElement(comp, props)}\n </ClientOnly>\n )\n }\n return React.createElement(comp, props)\n }\n\n ;(lazyComp as any).preload = load\n\n return lazyComp as any\n}\n"],"names":["jsx","ClientOnly","Outlet","React"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAUA,SAAS,sBAAsB,OAAqB;AAIlD,MAAI,QAAO,+BAAO,aAAY,SAAiB,QAAA;AAC/C,SACE,MAAM,QAAQ,WAAW,6CAA6C,KACtE,MAAM,QAAQ,WAAW,2CAA2C,KACpE,MAAM,QAAQ,WAAW,kCAAkC;AAE/D;AAEgB,SAAA,mBAId,UACA,YACA,KAGQ;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AAEJ,QAAM,OAAO,MAAM;AACjB,QAAI,OAAO,aAAa,gBAAe,kCAAY,OAAO;AACxD,aAAQ,MAAM;AACd,aAAO,QAAQ,QAAQ;AAAA,IAAA;AAEzB,QAAI,CAAC,aAAa;AAChB,oBAAc,SAAS,EACpB,KAAK,CAAC,QAAQ;AACC,sBAAA;AACP,eAAA,IAAI,cAAc,SAAS;AAAA,MAAA,CACnC,EACA,MAAM,CAAC,QAAQ;AAIN,gBAAA;AACJ,YAAA,sBAAsB,KAAK,GAAG;AAChC,cACE,iBAAiB,SACjB,OAAO,WAAW,eAClB,OAAO,mBAAmB,aAC1B;AAKM,kBAAA,aAAa,0BAA0B,MAAM,OAAO;AAC1D,gBAAI,CAAC,eAAe,QAAQ,UAAU,GAAG;AACxB,6BAAA,QAAQ,YAAY,GAAG;AAC7B,uBAAA;AAAA,YAAA;AAAA,UACX;AAAA,QACF;AAAA,MACF,CACD;AAAA,IAAA;AAGE,WAAA;AAAA,EACT;AAEM,QAAA,WAAW,SAAS,KAAK,OAAY;AAEzC,QAAI,QAAQ;AAGV,aAAO,SAAS,OAAO;AACjB,YAAA,IAAI,QAAQ,MAAM;AAAA,MAAA,CAAE;AAAA,IAAA;AAE5B,QAAI,OAAO;AAEH,YAAA;AAAA,IAAA;AAGR,QAAI,CAAC,MAAM;AACT,YAAM,KAAK;AAAA,IAAA;AAGT,SAAA,kCAAY,OAAO;AAEnB,aAAAA,2BAAA,IAACC,WAAW,YAAA,EAAA,UAAWD,2BAAAA,IAAAE,MAAAA,QAAA,CAAA,CAAO,GAC3B,UAAMC,iBAAA,cAAc,MAAM,KAAK,EAClC,CAAA;AAAA,IAAA;AAGG,WAAAA,iBAAM,cAAc,MAAM,KAAK;AAAA,EACxC;AAEE,WAAiB,UAAU;AAEtB,SAAA;AACT;;"}
@@ -1,7 +1,2 @@
1
1
  import { AsyncRouteComponent } from './route.cjs';
2
- import * as React from 'react';
3
- export declare function ClientOnly({ children, fallback, }: React.PropsWithChildren<{
4
- fallback?: React.ReactNode;
5
- }>): import("react/jsx-runtime").JSX.Element;
6
- export declare function useHydrated(): boolean;
7
2
  export declare function lazyRouteComponent<T extends Record<string, any>, TKey extends keyof T = 'default'>(importer: () => Promise<T>, exportName?: TKey, ssr?: () => boolean): T[TKey] extends (props: infer TProps) => any ? AsyncRouteComponent<TProps> : never;
@@ -0,0 +1,29 @@
1
+ import { default as React } from 'react';
2
+ export interface ClientOnlyProps {
3
+ /**
4
+ * The children to render if the JS is loaded.
5
+ */
6
+ children: React.ReactNode;
7
+ /**
8
+ * The fallback component to render if the JS is not yet loaded.
9
+ */
10
+ fallback?: React.ReactNode;
11
+ }
12
+ /**
13
+ * Render the children only after the JS has loaded client-side. Use an optional
14
+ * fallback component if the JS is not yet loaded.
15
+ *
16
+ * @example
17
+ * Render a Chart component if JS loads, renders a simple FakeChart
18
+ * component server-side or if there is no JS. The FakeChart can have only the
19
+ * UI without the behavior or be a loading spinner or skeleton.
20
+ *
21
+ * ```tsx
22
+ * return (
23
+ * <ClientOnly fallback={<FakeChart />}>
24
+ * <Chart />
25
+ * </ClientOnly>
26
+ * )
27
+ * ```
28
+ */
29
+ export declare function ClientOnly({ children, fallback }: ClientOnlyProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,20 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import React__default from "react";
3
+ function ClientOnly({ children, fallback = null }) {
4
+ return useHydrated() ? /* @__PURE__ */ jsx(React__default.Fragment, { children }) : /* @__PURE__ */ jsx(React__default.Fragment, { children: fallback });
5
+ }
6
+ function useHydrated() {
7
+ return React__default.useSyncExternalStore(
8
+ subscribe,
9
+ () => true,
10
+ () => false
11
+ );
12
+ }
13
+ function subscribe() {
14
+ return () => {
15
+ };
16
+ }
17
+ export {
18
+ ClientOnly
19
+ };
20
+ //# sourceMappingURL=ClientOnly.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClientOnly.js","sources":["../../src/ClientOnly.tsx"],"sourcesContent":["import React from 'react'\n\nexport interface ClientOnlyProps {\n /**\n * The children to render if the JS is loaded.\n */\n children: React.ReactNode\n /**\n * The fallback component to render if the JS is not yet loaded.\n */\n fallback?: React.ReactNode\n}\n\n/**\n * Render the children only after the JS has loaded client-side. Use an optional\n * fallback component if the JS is not yet loaded.\n *\n * @example\n * Render a Chart component if JS loads, renders a simple FakeChart\n * component server-side or if there is no JS. The FakeChart can have only the\n * UI without the behavior or be a loading spinner or skeleton.\n *\n * ```tsx\n * return (\n * <ClientOnly fallback={<FakeChart />}>\n * <Chart />\n * </ClientOnly>\n * )\n * ```\n */\nexport function ClientOnly({ children, fallback = null }: ClientOnlyProps) {\n return useHydrated() ? (\n <React.Fragment>{children}</React.Fragment>\n ) : (\n <React.Fragment>{fallback}</React.Fragment>\n )\n}\n\n/**\n * Return a boolean indicating if the JS has been hydrated already.\n * When doing Server-Side Rendering, the result will always be false.\n * When doing Client-Side Rendering, the result will always be false on the\n * first render and true from then on. Even if a new component renders it will\n * always start with true.\n *\n * @example\n * ```tsx\n * // Disable a button that needs JS to work.\n * let hydrated = useHydrated()\n * return (\n * <button type=\"button\" disabled={!hydrated} onClick={doSomethingCustom}>\n * Click me\n * </button>\n * )\n * ```\n * @returns True if the JS has been hydrated already, false otherwise.\n */\nfunction useHydrated(): boolean {\n return React.useSyncExternalStore(\n subscribe,\n () => true,\n () => false,\n )\n}\n\nfunction subscribe() {\n return () => {}\n}\n"],"names":["React"],"mappings":";;AA8BO,SAAS,WAAW,EAAE,UAAU,WAAW,QAAyB;AACzE,SAAO,YAAY,IAChB,oBAAAA,eAAM,UAAN,EAAgB,SAAS,CAAA,IAEzB,oBAAAA,eAAM,UAAN,EAAgB,UAAS,SAAA,CAAA;AAE9B;AAqBA,SAAS,cAAuB;AAC9B,SAAOA,eAAM;AAAA,IACX;AAAA,IACA,MAAM;AAAA,IACN,MAAM;AAAA,EACR;AACF;AAEA,SAAS,YAAY;AACnB,SAAO,MAAM;AAAA,EAAC;AAChB;"}
@@ -9,6 +9,7 @@ export type { BlockerFn, HistoryLocation, RouterHistory, ParsedPath, HistoryStat
9
9
  export { useAwaited, Await } from './awaited.js';
10
10
  export type { AwaitOptions } from './awaited.js';
11
11
  export { CatchBoundary, ErrorComponent } from './CatchBoundary.js';
12
+ export { ClientOnly } from './ClientOnly.js';
12
13
  export { FileRoute, createFileRoute, FileRouteLoader, LazyRoute, createLazyRoute, createLazyFileRoute, } from './fileRoute.js';
13
14
  export * from './history.js';
14
15
  export { lazyRouteComponent } from './lazyRouteComponent.js';
package/dist/esm/index.js CHANGED
@@ -4,6 +4,7 @@ import { PathParamError, SearchParamError, TSR_DEFERRED_PROMISE, cleanPath, comp
4
4
  import { createBrowserHistory, createHashHistory, createHistory, createMemoryHistory } from "@tanstack/history";
5
5
  import { Await, useAwaited } from "./awaited.js";
6
6
  import { CatchBoundary, ErrorComponent } from "./CatchBoundary.js";
7
+ import { ClientOnly } from "./ClientOnly.js";
7
8
  import { FileRoute, FileRouteLoader, LazyRoute, createFileRoute, createLazyFileRoute, createLazyRoute } from "./fileRoute.js";
8
9
  import { lazyRouteComponent } from "./lazyRouteComponent.js";
9
10
  import { Link, createLink, linkOptions, useLinkProps } from "./link.js";
@@ -39,6 +40,7 @@ export {
39
40
  Block,
40
41
  CatchBoundary,
41
42
  CatchNotFound,
43
+ ClientOnly,
42
44
  DefaultGlobalNotFound,
43
45
  ErrorComponent,
44
46
  FileRoute,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,7 +1,2 @@
1
1
  import { AsyncRouteComponent } from './route.js';
2
- import * as React from 'react';
3
- export declare function ClientOnly({ children, fallback, }: React.PropsWithChildren<{
4
- fallback?: React.ReactNode;
5
- }>): import("react/jsx-runtime").JSX.Element;
6
- export declare function useHydrated(): boolean;
7
2
  export declare function lazyRouteComponent<T extends Record<string, any>, TKey extends keyof T = 'default'>(importer: () => Promise<T>, exportName?: TKey, ssr?: () => boolean): T[TKey] extends (props: infer TProps) => any ? AsyncRouteComponent<TProps> : never;
@@ -1,27 +1,11 @@
1
- import { jsx, Fragment } from "react/jsx-runtime";
1
+ import { jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { Outlet } from "./Match.js";
4
+ import { ClientOnly } from "./ClientOnly.js";
4
5
  function isModuleNotFoundError(error) {
5
6
  if (typeof (error == null ? void 0 : error.message) !== "string") return false;
6
7
  return error.message.startsWith("Failed to fetch dynamically imported module") || error.message.startsWith("error loading dynamically imported module") || error.message.startsWith("Importing a module script failed");
7
8
  }
8
- function ClientOnly({
9
- children,
10
- fallback = null
11
- }) {
12
- return useHydrated() ? /* @__PURE__ */ jsx(Fragment, { children }) : /* @__PURE__ */ jsx(Fragment, { children: fallback });
13
- }
14
- function subscribe() {
15
- return () => {
16
- };
17
- }
18
- function useHydrated() {
19
- return React.useSyncExternalStore(
20
- subscribe,
21
- () => true,
22
- () => false
23
- );
24
- }
25
9
  function lazyRouteComponent(importer, exportName, ssr) {
26
10
  let loadPromise;
27
11
  let comp;
@@ -72,8 +56,6 @@ function lazyRouteComponent(importer, exportName, ssr) {
72
56
  return lazyComp;
73
57
  }
74
58
  export {
75
- ClientOnly,
76
- lazyRouteComponent,
77
- useHydrated
59
+ lazyRouteComponent
78
60
  };
79
61
  //# sourceMappingURL=lazyRouteComponent.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"lazyRouteComponent.js","sources":["../../src/lazyRouteComponent.tsx"],"sourcesContent":["import * as React from 'react'\nimport { Outlet } from './Match'\nimport type { AsyncRouteComponent } from './route'\n\n// If the load fails due to module not found, it may mean a new version of\n// the build was deployed and the user's browser is still using an old version.\n// If this happens, the old version in the user's browser would have an outdated\n// URL to the lazy module.\n// In that case, we want to attempt one window refresh to get the latest.\nfunction isModuleNotFoundError(error: any): boolean {\n // chrome: \"Failed to fetch dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // firefox: \"error loading dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // safari: \"Importing a module script failed.\"\n if (typeof error?.message !== 'string') return false\n return (\n error.message.startsWith('Failed to fetch dynamically imported module') ||\n error.message.startsWith('error loading dynamically imported module') ||\n error.message.startsWith('Importing a module script failed')\n )\n}\n\nexport function ClientOnly({\n children,\n fallback = null,\n}: React.PropsWithChildren<{ fallback?: React.ReactNode }>) {\n return useHydrated() ? <>{children}</> : <>{fallback}</>\n}\n\nfunction subscribe() {\n return () => {}\n}\n\nexport function useHydrated() {\n return React.useSyncExternalStore(\n subscribe,\n () => true,\n () => false,\n )\n}\n\nexport function lazyRouteComponent<\n T extends Record<string, any>,\n TKey extends keyof T = 'default',\n>(\n importer: () => Promise<T>,\n exportName?: TKey,\n ssr?: () => boolean,\n): T[TKey] extends (props: infer TProps) => any\n ? AsyncRouteComponent<TProps>\n : never {\n let loadPromise: Promise<any> | undefined\n let comp: T[TKey] | T['default']\n let error: any\n let reload: boolean\n\n const load = () => {\n if (typeof document === 'undefined' && ssr?.() === false) {\n comp = (() => null) as any\n return Promise.resolve()\n }\n if (!loadPromise) {\n loadPromise = importer()\n .then((res) => {\n loadPromise = undefined\n comp = res[exportName ?? 'default']\n })\n .catch((err) => {\n // We don't want an error thrown from preload in this case, because\n // there's nothing we want to do about module not found during preload.\n // Record the error, the rest is handled during the render path.\n error = err\n if (isModuleNotFoundError(error)) {\n if (\n error instanceof Error &&\n typeof window !== 'undefined' &&\n typeof sessionStorage !== 'undefined'\n ) {\n // Again, we want to reload one time on module not found error and not enter\n // a reload loop if there is some other issue besides an old deploy.\n // That's why we store our reload attempt in sessionStorage.\n // Use error.message as key because it contains the module path that failed.\n const storageKey = `tanstack_router_reload:${error.message}`\n if (!sessionStorage.getItem(storageKey)) {\n sessionStorage.setItem(storageKey, '1')\n reload = true\n }\n }\n }\n })\n }\n\n return loadPromise\n }\n\n const lazyComp = function Lazy(props: any) {\n // Now that we're out of preload and into actual render path,\n if (reload) {\n // If it was a module loading error,\n // throw eternal suspense while we wait for window to reload\n window.location.reload()\n throw new Promise(() => {})\n }\n if (error) {\n // Otherwise, just throw the error\n throw error\n }\n\n if (!comp) {\n throw load()\n }\n\n if (ssr?.() === false) {\n return (\n <ClientOnly fallback={<Outlet />}>\n {React.createElement(comp, props)}\n </ClientOnly>\n )\n }\n return React.createElement(comp, props)\n }\n\n ;(lazyComp as any).preload = load\n\n return lazyComp as any\n}\n"],"names":[],"mappings":";;;AASA,SAAS,sBAAsB,OAAqB;AAIlD,MAAI,QAAO,+BAAO,aAAY,SAAiB,QAAA;AAC/C,SACE,MAAM,QAAQ,WAAW,6CAA6C,KACtE,MAAM,QAAQ,WAAW,2CAA2C,KACpE,MAAM,QAAQ,WAAW,kCAAkC;AAE/D;AAEO,SAAS,WAAW;AAAA,EACzB;AAAA,EACA,WAAW;AACb,GAA4D;AAC1D,SAAO,YAAgB,IAAA,oBAAA,UAAA,EAAG,SAAS,CAAA,oCAAS,UAAS,SAAA,CAAA;AACvD;AAEA,SAAS,YAAY;AACnB,SAAO,MAAM;AAAA,EAAC;AAChB;AAEO,SAAS,cAAc;AAC5B,SAAO,MAAM;AAAA,IACX;AAAA,IACA,MAAM;AAAA,IACN,MAAM;AAAA,EACR;AACF;AAEgB,SAAA,mBAId,UACA,YACA,KAGQ;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AAEJ,QAAM,OAAO,MAAM;AACjB,QAAI,OAAO,aAAa,gBAAe,kCAAY,OAAO;AACxD,aAAQ,MAAM;AACd,aAAO,QAAQ,QAAQ;AAAA,IAAA;AAEzB,QAAI,CAAC,aAAa;AAChB,oBAAc,SAAS,EACpB,KAAK,CAAC,QAAQ;AACC,sBAAA;AACP,eAAA,IAAI,cAAc,SAAS;AAAA,MAAA,CACnC,EACA,MAAM,CAAC,QAAQ;AAIN,gBAAA;AACJ,YAAA,sBAAsB,KAAK,GAAG;AAChC,cACE,iBAAiB,SACjB,OAAO,WAAW,eAClB,OAAO,mBAAmB,aAC1B;AAKM,kBAAA,aAAa,0BAA0B,MAAM,OAAO;AAC1D,gBAAI,CAAC,eAAe,QAAQ,UAAU,GAAG;AACxB,6BAAA,QAAQ,YAAY,GAAG;AAC7B,uBAAA;AAAA,YAAA;AAAA,UACX;AAAA,QACF;AAAA,MACF,CACD;AAAA,IAAA;AAGE,WAAA;AAAA,EACT;AAEM,QAAA,WAAW,SAAS,KAAK,OAAY;AAEzC,QAAI,QAAQ;AAGV,aAAO,SAAS,OAAO;AACjB,YAAA,IAAI,QAAQ,MAAM;AAAA,MAAA,CAAE;AAAA,IAAA;AAE5B,QAAI,OAAO;AAEH,YAAA;AAAA,IAAA;AAGR,QAAI,CAAC,MAAM;AACT,YAAM,KAAK;AAAA,IAAA;AAGT,SAAA,kCAAY,OAAO;AAEnB,aAAA,oBAAC,YAAW,EAAA,UAAW,oBAAA,QAAA,CAAA,CAAO,GAC3B,UAAM,MAAA,cAAc,MAAM,KAAK,EAClC,CAAA;AAAA,IAAA;AAGG,WAAA,MAAM,cAAc,MAAM,KAAK;AAAA,EACxC;AAEE,WAAiB,UAAU;AAEtB,SAAA;AACT;"}
1
+ {"version":3,"file":"lazyRouteComponent.js","sources":["../../src/lazyRouteComponent.tsx"],"sourcesContent":["import * as React from 'react'\nimport { Outlet } from './Match'\nimport { ClientOnly } from './ClientOnly'\nimport type { AsyncRouteComponent } from './route'\n\n// If the load fails due to module not found, it may mean a new version of\n// the build was deployed and the user's browser is still using an old version.\n// If this happens, the old version in the user's browser would have an outdated\n// URL to the lazy module.\n// In that case, we want to attempt one window refresh to get the latest.\nfunction isModuleNotFoundError(error: any): boolean {\n // chrome: \"Failed to fetch dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // firefox: \"error loading dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // safari: \"Importing a module script failed.\"\n if (typeof error?.message !== 'string') return false\n return (\n error.message.startsWith('Failed to fetch dynamically imported module') ||\n error.message.startsWith('error loading dynamically imported module') ||\n error.message.startsWith('Importing a module script failed')\n )\n}\n\nexport function lazyRouteComponent<\n T extends Record<string, any>,\n TKey extends keyof T = 'default',\n>(\n importer: () => Promise<T>,\n exportName?: TKey,\n ssr?: () => boolean,\n): T[TKey] extends (props: infer TProps) => any\n ? AsyncRouteComponent<TProps>\n : never {\n let loadPromise: Promise<any> | undefined\n let comp: T[TKey] | T['default']\n let error: any\n let reload: boolean\n\n const load = () => {\n if (typeof document === 'undefined' && ssr?.() === false) {\n comp = (() => null) as any\n return Promise.resolve()\n }\n if (!loadPromise) {\n loadPromise = importer()\n .then((res) => {\n loadPromise = undefined\n comp = res[exportName ?? 'default']\n })\n .catch((err) => {\n // We don't want an error thrown from preload in this case, because\n // there's nothing we want to do about module not found during preload.\n // Record the error, the rest is handled during the render path.\n error = err\n if (isModuleNotFoundError(error)) {\n if (\n error instanceof Error &&\n typeof window !== 'undefined' &&\n typeof sessionStorage !== 'undefined'\n ) {\n // Again, we want to reload one time on module not found error and not enter\n // a reload loop if there is some other issue besides an old deploy.\n // That's why we store our reload attempt in sessionStorage.\n // Use error.message as key because it contains the module path that failed.\n const storageKey = `tanstack_router_reload:${error.message}`\n if (!sessionStorage.getItem(storageKey)) {\n sessionStorage.setItem(storageKey, '1')\n reload = true\n }\n }\n }\n })\n }\n\n return loadPromise\n }\n\n const lazyComp = function Lazy(props: any) {\n // Now that we're out of preload and into actual render path,\n if (reload) {\n // If it was a module loading error,\n // throw eternal suspense while we wait for window to reload\n window.location.reload()\n throw new Promise(() => {})\n }\n if (error) {\n // Otherwise, just throw the error\n throw error\n }\n\n if (!comp) {\n throw load()\n }\n\n if (ssr?.() === false) {\n return (\n <ClientOnly fallback={<Outlet />}>\n {React.createElement(comp, props)}\n </ClientOnly>\n )\n }\n return React.createElement(comp, props)\n }\n\n ;(lazyComp as any).preload = load\n\n return lazyComp as any\n}\n"],"names":[],"mappings":";;;;AAUA,SAAS,sBAAsB,OAAqB;AAIlD,MAAI,QAAO,+BAAO,aAAY,SAAiB,QAAA;AAC/C,SACE,MAAM,QAAQ,WAAW,6CAA6C,KACtE,MAAM,QAAQ,WAAW,2CAA2C,KACpE,MAAM,QAAQ,WAAW,kCAAkC;AAE/D;AAEgB,SAAA,mBAId,UACA,YACA,KAGQ;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AAEJ,QAAM,OAAO,MAAM;AACjB,QAAI,OAAO,aAAa,gBAAe,kCAAY,OAAO;AACxD,aAAQ,MAAM;AACd,aAAO,QAAQ,QAAQ;AAAA,IAAA;AAEzB,QAAI,CAAC,aAAa;AAChB,oBAAc,SAAS,EACpB,KAAK,CAAC,QAAQ;AACC,sBAAA;AACP,eAAA,IAAI,cAAc,SAAS;AAAA,MAAA,CACnC,EACA,MAAM,CAAC,QAAQ;AAIN,gBAAA;AACJ,YAAA,sBAAsB,KAAK,GAAG;AAChC,cACE,iBAAiB,SACjB,OAAO,WAAW,eAClB,OAAO,mBAAmB,aAC1B;AAKM,kBAAA,aAAa,0BAA0B,MAAM,OAAO;AAC1D,gBAAI,CAAC,eAAe,QAAQ,UAAU,GAAG;AACxB,6BAAA,QAAQ,YAAY,GAAG;AAC7B,uBAAA;AAAA,YAAA;AAAA,UACX;AAAA,QACF;AAAA,MACF,CACD;AAAA,IAAA;AAGE,WAAA;AAAA,EACT;AAEM,QAAA,WAAW,SAAS,KAAK,OAAY;AAEzC,QAAI,QAAQ;AAGV,aAAO,SAAS,OAAO;AACjB,YAAA,IAAI,QAAQ,MAAM;AAAA,MAAA,CAAE;AAAA,IAAA;AAE5B,QAAI,OAAO;AAEH,YAAA;AAAA,IAAA;AAGR,QAAI,CAAC,MAAM;AACT,YAAM,KAAK;AAAA,IAAA;AAGT,SAAA,kCAAY,OAAO;AAEnB,aAAA,oBAAC,YAAW,EAAA,UAAW,oBAAA,QAAA,CAAA,CAAO,GAC3B,UAAM,MAAA,cAAc,MAAM,KAAK,EAClC,CAAA;AAAA,IAAA;AAGG,WAAA,MAAM,cAAc,MAAM,KAAK;AAAA,EACxC;AAEE,WAAiB,UAAU;AAEtB,SAAA;AACT;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
- "version": "1.115.2",
3
+ "version": "1.116.0",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -54,7 +54,7 @@
54
54
  "tiny-invariant": "^1.3.3",
55
55
  "tiny-warning": "^1.0.3",
56
56
  "@tanstack/history": "1.115.0",
57
- "@tanstack/router-core": "1.115.0"
57
+ "@tanstack/router-core": "1.115.3"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@testing-library/jest-dom": "^6.6.3",
@@ -0,0 +1,68 @@
1
+ import React from 'react'
2
+
3
+ export interface ClientOnlyProps {
4
+ /**
5
+ * The children to render if the JS is loaded.
6
+ */
7
+ children: React.ReactNode
8
+ /**
9
+ * The fallback component to render if the JS is not yet loaded.
10
+ */
11
+ fallback?: React.ReactNode
12
+ }
13
+
14
+ /**
15
+ * Render the children only after the JS has loaded client-side. Use an optional
16
+ * fallback component if the JS is not yet loaded.
17
+ *
18
+ * @example
19
+ * Render a Chart component if JS loads, renders a simple FakeChart
20
+ * component server-side or if there is no JS. The FakeChart can have only the
21
+ * UI without the behavior or be a loading spinner or skeleton.
22
+ *
23
+ * ```tsx
24
+ * return (
25
+ * <ClientOnly fallback={<FakeChart />}>
26
+ * <Chart />
27
+ * </ClientOnly>
28
+ * )
29
+ * ```
30
+ */
31
+ export function ClientOnly({ children, fallback = null }: ClientOnlyProps) {
32
+ return useHydrated() ? (
33
+ <React.Fragment>{children}</React.Fragment>
34
+ ) : (
35
+ <React.Fragment>{fallback}</React.Fragment>
36
+ )
37
+ }
38
+
39
+ /**
40
+ * Return a boolean indicating if the JS has been hydrated already.
41
+ * When doing Server-Side Rendering, the result will always be false.
42
+ * When doing Client-Side Rendering, the result will always be false on the
43
+ * first render and true from then on. Even if a new component renders it will
44
+ * always start with true.
45
+ *
46
+ * @example
47
+ * ```tsx
48
+ * // Disable a button that needs JS to work.
49
+ * let hydrated = useHydrated()
50
+ * return (
51
+ * <button type="button" disabled={!hydrated} onClick={doSomethingCustom}>
52
+ * Click me
53
+ * </button>
54
+ * )
55
+ * ```
56
+ * @returns True if the JS has been hydrated already, false otherwise.
57
+ */
58
+ function useHydrated(): boolean {
59
+ return React.useSyncExternalStore(
60
+ subscribe,
61
+ () => true,
62
+ () => false,
63
+ )
64
+ }
65
+
66
+ function subscribe() {
67
+ return () => {}
68
+ }
package/src/index.tsx CHANGED
@@ -149,6 +149,7 @@ export { useAwaited, Await } from './awaited'
149
149
  export type { AwaitOptions } from './awaited'
150
150
 
151
151
  export { CatchBoundary, ErrorComponent } from './CatchBoundary'
152
+ export { ClientOnly } from './ClientOnly'
152
153
 
153
154
  export {
154
155
  FileRoute,
@@ -1,5 +1,6 @@
1
1
  import * as React from 'react'
2
2
  import { Outlet } from './Match'
3
+ import { ClientOnly } from './ClientOnly'
3
4
  import type { AsyncRouteComponent } from './route'
4
5
 
5
6
  // If the load fails due to module not found, it may mean a new version of
@@ -19,25 +20,6 @@ function isModuleNotFoundError(error: any): boolean {
19
20
  )
20
21
  }
21
22
 
22
- export function ClientOnly({
23
- children,
24
- fallback = null,
25
- }: React.PropsWithChildren<{ fallback?: React.ReactNode }>) {
26
- return useHydrated() ? <>{children}</> : <>{fallback}</>
27
- }
28
-
29
- function subscribe() {
30
- return () => {}
31
- }
32
-
33
- export function useHydrated() {
34
- return React.useSyncExternalStore(
35
- subscribe,
36
- () => true,
37
- () => false,
38
- )
39
- }
40
-
41
23
  export function lazyRouteComponent<
42
24
  T extends Record<string, any>,
43
25
  TKey extends keyof T = 'default',