From 6435bcb3e6bda712c27a911226d4104d23862236 Mon Sep 17 00:00:00 2001 From: daveycodez Date: Sun, 26 Oct 2025 16:57:33 -0700 Subject: [PATCH 1/4] Export useHydrated from ClientOnly modules The useHydrated hook is now exported from both react-router and solid-router ClientOnly modules and re-exported in their respective index files. This allows consumers to directly import useHydrated for hydration state detection. --- packages/react-router/src/ClientOnly.tsx | 2 +- packages/react-router/src/index.tsx | 2 +- packages/solid-router/src/ClientOnly.tsx | 2 +- packages/solid-router/src/index.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/react-router/src/ClientOnly.tsx b/packages/react-router/src/ClientOnly.tsx index 7269b92e0ce..be16ff2eee1 100644 --- a/packages/react-router/src/ClientOnly.tsx +++ b/packages/react-router/src/ClientOnly.tsx @@ -63,7 +63,7 @@ export function ClientOnly({ children, fallback = null }: ClientOnlyProps) { /** * Return a boolean indicating whether client hydration has occurred. */ -function useHydrated(): boolean { +export function useHydrated(): boolean { return React.useSyncExternalStore( subscribe, () => true, diff --git a/packages/react-router/src/index.tsx b/packages/react-router/src/index.tsx index 77270748939..29d293fe986 100644 --- a/packages/react-router/src/index.tsx +++ b/packages/react-router/src/index.tsx @@ -130,7 +130,7 @@ export { useAwaited, Await } from './awaited' export type { AwaitOptions } from './awaited' export { CatchBoundary, ErrorComponent } from './CatchBoundary' -export { ClientOnly } from './ClientOnly' +export { ClientOnly, useHydrated } from './ClientOnly' export { FileRoute, diff --git a/packages/solid-router/src/ClientOnly.tsx b/packages/solid-router/src/ClientOnly.tsx index e3e251a8489..6ed10d267b3 100644 --- a/packages/solid-router/src/ClientOnly.tsx +++ b/packages/solid-router/src/ClientOnly.tsx @@ -56,7 +56,7 @@ export function ClientOnly(props: ClientOnlyProps) { * ``` * @returns True if the JS has been hydrated already, false otherwise. */ -function useHydrated(): Solid.Accessor { +export function useHydrated(): Solid.Accessor { const [hydrated, setHydrated] = Solid.createSignal(false) Solid.onMount(() => { setHydrated(true) diff --git a/packages/solid-router/src/index.tsx b/packages/solid-router/src/index.tsx index 070216b839e..f3e4b635bef 100644 --- a/packages/solid-router/src/index.tsx +++ b/packages/solid-router/src/index.tsx @@ -211,7 +211,7 @@ export { useAwaited, Await } from './awaited' export type { AwaitOptions } from './awaited' export { CatchBoundary, ErrorComponent } from './CatchBoundary' -export { ClientOnly } from './ClientOnly' +export { ClientOnly, useHydrated } from './ClientOnly' export { FileRoute, From 3f5cdaf1bc97b42c034f6f4b4777c5640be4a957 Mon Sep 17 00:00:00 2001 From: daveycodez Date: Sun, 26 Oct 2025 16:59:19 -0700 Subject: [PATCH 2/4] Add useHydrated hook documentation to execution model guides Introduced documentation for the useHydrated hook in both React and Solid execution model guides. The new sections explain how to use the hook for hydration-dependent behavior, including example usage and details on its SSR and client behavior. --- .../framework/react/guide/execution-model.md | 29 +++++++++++++++++++ .../framework/solid/guide/execution-model.md | 29 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/docs/start/framework/react/guide/execution-model.md b/docs/start/framework/react/guide/execution-model.md index 03556345e93..5141248d56d 100644 --- a/docs/start/framework/react/guide/execution-model.md +++ b/docs/start/framework/react/guide/execution-model.md @@ -98,6 +98,35 @@ function Analytics() { } ``` +#### useHydrated Hook + +For more granular control over hydration-dependent behavior, use the `useHydrated` hook. It returns a boolean indicating whether the client has been hydrated: + +```tsx +import { useHydrated } from '@tanstack/react-router' + +function InteractiveButton() { + const hydrated = useHydrated() + + return ( + + ) +} +``` + +**Behavior:** +- **During SSR**: Always returns `false` +- **First client render**: Returns `false` +- **After hydration**: Returns `true` (and stays `true` for all subsequent renders) + +This is useful when you need to conditionally enable features that depend on client-side JavaScript being loaded, such as interactive elements, animations, or browser APIs. + ### Environment-Specific Implementations ```tsx diff --git a/docs/start/framework/solid/guide/execution-model.md b/docs/start/framework/solid/guide/execution-model.md index 98d9c2b7cad..5e653a3ffc3 100644 --- a/docs/start/framework/solid/guide/execution-model.md +++ b/docs/start/framework/solid/guide/execution-model.md @@ -98,6 +98,35 @@ function Analytics() { } ``` +#### useHydrated Hook + +For more granular control over hydration-dependent behavior, use the `useHydrated` hook. It returns an accessor (signal) indicating whether the client has been hydrated: + +```tsx +import { useHydrated } from '@tanstack/solid-router' + +function InteractiveButton() { + const hydrated = useHydrated() + + return ( + + ) +} +``` + +**Behavior:** +- **During SSR**: Always returns `false` +- **First client render**: Returns `false` +- **After hydration**: Returns `true` (and stays `true` for all subsequent renders) + +This is useful when you need to conditionally enable features that depend on client-side JavaScript being loaded, such as interactive elements, animations, or browser APIs. + ### Environment-Specific Implementations ```tsx From 6fcdcd91615ac2b05f39b67acf95c4fd1d0b030c Mon Sep 17 00:00:00 2001 From: daveycodez Date: Sun, 26 Oct 2025 17:06:52 -0700 Subject: [PATCH 3/4] Update hydration example to show timezone display Replaces the interactive button example with a timezone display in both React and Solid execution model guides. Updates the explanation to emphasize conditional rendering based on client-side data, providing a clearer use case for hydration-dependent behavior. --- .../framework/react/guide/execution-model.md | 17 ++++++----------- .../framework/solid/guide/execution-model.md | 17 ++++++----------- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/docs/start/framework/react/guide/execution-model.md b/docs/start/framework/react/guide/execution-model.md index 5141248d56d..3cb58c4b1b4 100644 --- a/docs/start/framework/react/guide/execution-model.md +++ b/docs/start/framework/react/guide/execution-model.md @@ -105,18 +105,13 @@ For more granular control over hydration-dependent behavior, use the `useHydrate ```tsx import { useHydrated } from '@tanstack/react-router' -function InteractiveButton() { +function TimeZoneDisplay() { const hydrated = useHydrated() + const timeZone = hydrated + ? Intl.DateTimeFormat().resolvedOptions().timeZone + : 'UTC' - return ( - - ) + return
Your timezone: {timeZone}
} ``` @@ -125,7 +120,7 @@ function InteractiveButton() { - **First client render**: Returns `false` - **After hydration**: Returns `true` (and stays `true` for all subsequent renders) -This is useful when you need to conditionally enable features that depend on client-side JavaScript being loaded, such as interactive elements, animations, or browser APIs. +This is useful when you need to conditionally render content based on client-side data (like browser timezone, locale, or localStorage) while providing a sensible fallback for server rendering. ### Environment-Specific Implementations diff --git a/docs/start/framework/solid/guide/execution-model.md b/docs/start/framework/solid/guide/execution-model.md index 5e653a3ffc3..7d94895e491 100644 --- a/docs/start/framework/solid/guide/execution-model.md +++ b/docs/start/framework/solid/guide/execution-model.md @@ -105,18 +105,13 @@ For more granular control over hydration-dependent behavior, use the `useHydrate ```tsx import { useHydrated } from '@tanstack/solid-router' -function InteractiveButton() { +function TimeZoneDisplay() { const hydrated = useHydrated() + const timeZone = () => hydrated() + ? Intl.DateTimeFormat().resolvedOptions().timeZone + : 'UTC' - return ( - - ) + return
Your timezone: {timeZone()}
} ``` @@ -125,7 +120,7 @@ function InteractiveButton() { - **First client render**: Returns `false` - **After hydration**: Returns `true` (and stays `true` for all subsequent renders) -This is useful when you need to conditionally enable features that depend on client-side JavaScript being loaded, such as interactive elements, animations, or browser APIs. +This is useful when you need to conditionally render content based on client-side data (like browser timezone, locale, or localStorage) while providing a sensible fallback for server rendering. ### Environment-Specific Implementations From 464faf633a2156bc68d0b6c1de3918039e467108 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 27 Oct 2025 18:54:29 +0000 Subject: [PATCH 4/4] ci: apply automated fixes --- docs/start/framework/react/guide/execution-model.md | 7 ++++--- docs/start/framework/solid/guide/execution-model.md | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/start/framework/react/guide/execution-model.md b/docs/start/framework/react/guide/execution-model.md index 3cb58c4b1b4..ab78c6fed5f 100644 --- a/docs/start/framework/react/guide/execution-model.md +++ b/docs/start/framework/react/guide/execution-model.md @@ -107,15 +107,16 @@ import { useHydrated } from '@tanstack/react-router' function TimeZoneDisplay() { const hydrated = useHydrated() - const timeZone = hydrated - ? Intl.DateTimeFormat().resolvedOptions().timeZone + const timeZone = hydrated + ? Intl.DateTimeFormat().resolvedOptions().timeZone : 'UTC' - + return
Your timezone: {timeZone}
} ``` **Behavior:** + - **During SSR**: Always returns `false` - **First client render**: Returns `false` - **After hydration**: Returns `true` (and stays `true` for all subsequent renders) diff --git a/docs/start/framework/solid/guide/execution-model.md b/docs/start/framework/solid/guide/execution-model.md index 7d94895e491..c2cedb167a2 100644 --- a/docs/start/framework/solid/guide/execution-model.md +++ b/docs/start/framework/solid/guide/execution-model.md @@ -107,15 +107,15 @@ import { useHydrated } from '@tanstack/solid-router' function TimeZoneDisplay() { const hydrated = useHydrated() - const timeZone = () => hydrated() - ? Intl.DateTimeFormat().resolvedOptions().timeZone - : 'UTC' - + const timeZone = () => + hydrated() ? Intl.DateTimeFormat().resolvedOptions().timeZone : 'UTC' + return
Your timezone: {timeZone()}
} ``` **Behavior:** + - **During SSR**: Always returns `false` - **First client render**: Returns `false` - **After hydration**: Returns `true` (and stays `true` for all subsequent renders)