Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/start/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@
"label": "Server Routes",
"to": "framework/react/guide/server-routes"
},
{
"label": "Hydration Errors",
"to": "framework/react/guide/hydration-errors"
},
{
"label": "Selective SSR",
"to": "framework/react/guide/selective-ssr"
Expand Down Expand Up @@ -190,6 +194,10 @@
"label": "Server Routes",
"to": "framework/solid/guide/server-routes"
},
{
"label": "Hydration Errors",
"to": "framework/solid/guide/hydration-errors"
},
{
"label": "Selective SSR",
"to": "framework/solid/guide/selective-ssr"
Expand Down
122 changes: 122 additions & 0 deletions docs/start/framework/react/guide/hydration-errors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
---
id: hydration-errors
title: Hydration Errors
---

### Why it happens
- **Mismatch**: Server HTML differs from client render during hydration
- **Common causes**: `Intl` (locale/time zone), `Date.now()`, random IDs, responsive-only logic, feature flags, user prefs

### Strategy 1 — Make server and client match
- **Pick a deterministic locale/time zone on the server** and use the same on the client
- **Source of truth**: cookie (preferred) or `Accept-Language` header
- **Compute once on the server** and hydrate as initial state

```tsx
// src/start.ts
import { createStart, createMiddleware } from '@tanstack/react-start'
import { getRequestHeader, getCookie, setCookie } from '@tanstack/react-start/server'

const localeTzMiddleware = createMiddleware().server(async ({ next }) => {
const header = getRequestHeader('accept-language')
const headerLocale = header?.split(',')[0] || 'en-US'
const cookieLocale = getCookie('locale')
const cookieTz = getCookie('tz') // set by client later (see Strategy 2)

const locale = cookieLocale || headerLocale
const timeZone = cookieTz || 'UTC' // deterministic until client sends tz

// Persist locale for subsequent requests (optional)
setCookie('locale', locale, { path: '/', maxAge: 60 * 60 * 24 * 365 })

return next({ context: { locale, timeZone } })
})

export const startInstance = createStart(() => ({
requestMiddleware: [localeTzMiddleware],
}))
```

```tsx
// src/routes/index.tsx (example)
import * as React from 'react'
import { createFileRoute } from '@tanstack/react-router'
import { createServerFn } from '@tanstack/react-start'
import { getCookie } from '@tanstack/react-start/server'

export const getServerNow = createServerFn().handler(async () => {
const locale = getCookie('locale') || 'en-US'
const timeZone = getCookie('tz') || 'UTC'
return new Intl.DateTimeFormat(locale, { dateStyle: 'medium', timeStyle: 'short', timeZone }).format(new Date())
})

export const Route = createFileRoute('/')({
loader: () => getServerNow(),
component: () => {
const serverNow = Route.useLoaderData() as string
return <time dateTime={serverNow}>{serverNow}</time>
},
})
```

### Strategy 2 — Let the client tell you its environment
- On first visit, set a cookie with the client time zone; SSR uses `UTC` until then
- Do this without risking mismatches

```tsx
import * as React from 'react'
import { ClientOnly } from '@tanstack/react-router'

function SetTimeZoneCookie() {
React.useEffect(() => {
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone
document.cookie = `tz=${tz}; path=/; max-age=31536000`
}, [])
return null
}

export function AppBoot() {
return (
<ClientOnly fallback={null}>
<SetTimeZoneCookie />
</ClientOnly>
)
}
```

### Strategy 3 — Make it client-only
- Wrap unstable UI in `<ClientOnly>` to avoid SSR and mismatches

```tsx
import { ClientOnly } from '@tanstack/react-router'

<ClientOnly fallback={<span>—</span>}>
<RelativeTime ts={someTs} />
</ClientOnly>
```

### Strategy 4 — Disable or limit SSR for the route
- Use Selective SSR to avoid rendering the component on the server

```tsx
export const Route = createFileRoute('/unstable')({
ssr: 'data-only', // or false
component: () => <ExpensiveViz />,
})
```

### Strategy 5 — Last resort suppression
- For small, known-different nodes, you can use React’s `suppressHydrationWarning`

```tsx
<time suppressHydrationWarning>{new Date().toLocaleString()}</time>
```

### Checklist
- **Deterministic inputs**: locale, time zone, feature flags
- **Prefer cookies** for client context; fallback to `Accept-Language`
- **Use `<ClientOnly>`** for inherently dynamic UI
- **Use Selective SSR** when server HTML cannot be stable
- **Avoid blind suppression**; use `suppressHydrationWarning` sparingly

See also: [Execution Model](../execution-model.md), [Code Execution Patterns](../code-execution-patterns.md), [Selective SSR](../selective-ssr.md), [Server Functions](../server-functions.md)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix doc link format to comply with coding guidelines.

The "See also" links use relative file paths (../execution-model.md) instead of doc-friendly links relative to the docs folder. Per the coding guidelines, use paths like ./guide/execution-model (without .md extension).

Apply this diff:

-See also: [Execution Model](../execution-model.md), [Code Execution Patterns](../code-execution-patterns.md), [Selective SSR](../selective-ssr.md), [Server Functions](../server-functions.md)
+See also: [Execution Model](./execution-model), [Code Execution Patterns](./code-execution-patterns), [Selective SSR](./selective-ssr), [Server Functions](./server-functions)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
See also: [Execution Model](../execution-model.md), [Code Execution Patterns](../code-execution-patterns.md), [Selective SSR](../selective-ssr.md), [Server Functions](../server-functions.md)
See also: [Execution Model](./execution-model), [Code Execution Patterns](./code-execution-patterns), [Selective SSR](./selective-ssr), [Server Functions](./server-functions)
🤖 Prompt for AI Agents
In docs/start/framework/react/guide/hydration-errors.md around line 122, the
"See also" links use relative file paths with .md and parent directory traversal
(e.g. ../execution-model.md); update each link to the doc-friendly form without
the .md and using a docs-relative path (for example replace
../execution-model.md with ./guide/execution-model,
../code-execution-patterns.md with ./guide/code-execution-patterns,
../selective-ssr.md with ./guide/selective-ssr, and ../server-functions.md with
./guide/server-functions) so links conform to the coding guidelines.

114 changes: 114 additions & 0 deletions docs/start/framework/solid/guide/hydration-errors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
id: hydration-errors
title: Hydration Errors
---

### Why it happens
- **Mismatch**: Server HTML differs from client render during hydration
- **Common causes**: `Intl` (locale/time zone), `Date.now()`, random IDs, responsive-only logic, feature flags, user prefs

### Strategy 1 — Make server and client match
- **Pick a deterministic locale/time zone on the server** and use the same on the client
- **Source of truth**: cookie (preferred) or `Accept-Language` header
- **Compute once on the server** and hydrate as initial state

```tsx
// src/start.ts
import { createStart, createMiddleware } from '@tanstack/solid-start'
import { getRequestHeader, getCookie, setCookie } from '@tanstack/solid-start/server'

const localeTzMiddleware = createMiddleware().server(async ({ next }) => {
const header = getRequestHeader('accept-language')
const headerLocale = header?.split(',')[0] || 'en-US'
const cookieLocale = getCookie('locale')
const cookieTz = getCookie('tz') // set by client later (see Strategy 2)

const locale = cookieLocale || headerLocale
const timeZone = cookieTz || 'UTC'

setCookie('locale', locale, { path: '/', maxAge: 60 * 60 * 24 * 365 })

return next({ context: { locale, timeZone } })
})

export const startInstance = createStart(() => ({
requestMiddleware: [localeTzMiddleware],
}))
```

```tsx
// src/routes/index.tsx (example)
import { createFileRoute } from '@tanstack/solid-router'
import { createServerFn } from '@tanstack/solid-start'
import { getCookie } from '@tanstack/solid-start/server'

export const getServerNow = createServerFn().handler(async () => {
const locale = getCookie('locale') || 'en-US'
const timeZone = getCookie('tz') || 'UTC'
return new Intl.DateTimeFormat(locale, { dateStyle: 'medium', timeStyle: 'short', timeZone }).format(new Date())
})

export const Route = createFileRoute('/')({
loader: () => getServerNow(),
component: () => {
const serverNow = Route.useLoaderData() as string
return <time dateTime={serverNow}>{serverNow}</time>
},
})
```

### Strategy 2 — Let the client tell you its environment
- On first visit, set a cookie with the client time zone; SSR uses `UTC` until then

```tsx
import { ClientOnly } from '@tanstack/solid-router'

function SetTimeZoneCookie() {
if (typeof window !== 'undefined') {
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone
document.cookie = `tz=${tz}; path=/; max-age=31536000`
}
return null
}

<ClientOnly fallback={null}>
<SetTimeZoneCookie />
</ClientOnly>
```

### Strategy 3 — Make it client-only
- Use `<ClientOnly>` or Solid’s `<NoHydration>` to avoid SSR/hydration

```tsx
import { ClientOnly } from '@tanstack/solid-router'
import { NoHydration } from 'solid-js/web'

<ClientOnly fallback={<span>—</span>}>
<RelativeTime ts={someTs} />
</ClientOnly>

<NoHydration>
<time>{new Date().toLocaleString()}</time>
</NoHydration>
```

### Strategy 4 — Disable or limit SSR for the route
- Use Selective SSR to avoid rendering the component on the server

```tsx
export const Route = createFileRoute('/unstable')({
ssr: 'data-only', // or false
component: () => <ExpensiveViz />,
})
```

### Strategy 5 — Last resort suppression
- Prefer the tools above; avoid mismatches instead of hiding them

### Checklist
- **Deterministic inputs**: locale, time zone, feature flags
- **Prefer cookies** for client context; fallback to `Accept-Language`
- **Use `<ClientOnly>`/`<NoHydration>`** for inherently dynamic UI
- **Use Selective SSR** when server HTML cannot be stable

See also: [Execution Model](../execution-model.md), [Code Execution Patterns](../code-execution-patterns.md), [Selective SSR](../selective-ssr.md), [Server Functions](../server-functions.md)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix doc link format to comply with coding guidelines.

The "See also" links use relative file paths (../execution-model.md) instead of doc-friendly links relative to the docs folder. Per the coding guidelines, use paths like ./guide/execution-model (without .md extension). This should match the format applied to the React version of this guide.

Apply this diff:

-See also: [Execution Model](../execution-model.md), [Code Execution Patterns](../code-execution-patterns.md), [Selective SSR](../selective-ssr.md), [Server Functions](../server-functions.md)
+See also: [Execution Model](./execution-model), [Code Execution Patterns](./code-execution-patterns), [Selective SSR](./selective-ssr), [Server Functions](./server-functions)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
See also: [Execution Model](../execution-model.md), [Code Execution Patterns](../code-execution-patterns.md), [Selective SSR](../selective-ssr.md), [Server Functions](../server-functions.md)
See also: [Execution Model](./execution-model), [Code Execution Patterns](./code-execution-patterns), [Selective SSR](./selective-ssr), [Server Functions](./server-functions)
🤖 Prompt for AI Agents
In docs/start/framework/solid/guide/hydration-errors.md around line 114, the
"See also" links use relative file paths with .md and parent directories; update
each link to the docs-friendly path format (no .md, paths relative to the docs
folder) — replace ../execution-model.md with ./guide/execution-model,
../code-execution-patterns.md with ./guide/code-execution-patterns,
../selective-ssr.md with ./guide/selective-ssr, and ../server-functions.md with
./guide/server-functions so the link format matches the React guide.

Loading