Skip to content

Title: fix(react-db): add getServerSnapshot to useSyncExternalStore for React 19 SSR#1534

Open
0Serenvale wants to merge 1 commit into
TanStack:mainfrom
0Serenvale:fix/react-19-ssr-server-snapshot
Open

Title: fix(react-db): add getServerSnapshot to useSyncExternalStore for React 19 SSR#1534
0Serenvale wants to merge 1 commit into
TanStack:mainfrom
0Serenvale:fix/react-19-ssr-server-snapshot

Conversation

@0Serenvale
Copy link
Copy Markdown

@0Serenvale 0Serenvale commented May 18, 2026

React 19 requires useSyncExternalStore to receive a third argument (getServerSnapshot) when used in SSR/server component contexts (Next.js App Router). Without it, the app throws during hydration or enters an infinite render loop.

The fix adds a module-level SERVER_SNAPSHOT constant passed as the third argument. It must be module-level — a new object created inside the hook on each render would fail React 19's stability check and cause an infinite loop.

Reproduces with: Next.js 15/16 + React 19 + @tanstack/react-db in any SSR route that uses useLiveQuery.

🎯 Changes

  • Added SERVER_SNAPSHOT module-level constant in useLiveQuery
  • Passed it as the third argument to useSyncExternalStore to satisfy React 19's SSR requirements
  • Prevents hydration errors and infinite render loops in Next.js App Router with React 19

✅ Checklist

  • Fix verified in Next.js 16 + React 19 App Router environment
  • No changeset needed — patch fix to existing hook, no API surface change
  • Existing tests pass

🚀 Release Impact

Patch release. No API changes. Fixes a crash/loop for all users on React 19 + SSR.

…t 19 SSR

React 19 requires useSyncExternalStore to receive a third argument
(getServerSnapshot) when used in SSR/server component contexts. Without it,
Next.js App Router throws during hydration or enters an infinite render loop.

The fix adds a module-level SERVER_SNAPSHOT constant and passes it as the
third argument. It must be module-level — a new object created inside the
hook on each call would fail React 19's stability check and loop.

Fixes SSR usage in Next.js 15+ with React 19.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 18, 2026

📝 Walkthrough

Walkthrough

The useLiveQuery.ts hook is updated to address React 19 SSR/hydration compatibility by introducing a module-level stable SERVER_SNAPSHOT constant and configuring useSyncExternalStore to use it as the server snapshot reference during rendering.

Changes

React 19 SSR/hydration server snapshot stability

Layer / File(s) Summary
Stable server snapshot for useSyncExternalStore
packages/react-db/src/useLiveQuery.ts
A module-level SERVER_SNAPSHOT constant provides a stable object reference, and useSyncExternalStore is configured with a server snapshot getter function that returns this constant, ensuring consistent identity across server renders and client hydration.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Poem

🐰 A snapshot stable as a carrot so fine,
React's hydration now reads the same line,
No new objects each render to chase,
Server and client agree on the base! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description provides clear context about the problem and solution, but does not follow the provided template structure with required sections like 'Changes' and 'Checklist'. Restructure the PR description to match the template: add a '## 🎯 Changes' section describing the change and motivation, and include a '## ✅ Checklist' section with test verification and a '## 🚀 Release Impact' section indicating whether a changeset was generated.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding getServerSnapshot to useSyncExternalStore to fix React 19 SSR support.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/react-db/src/useLiveQuery.ts (1)

23-26: 💤 Low value

The as const assertion is unnecessary for reference stability.

The comment states the constant is module-level for a "stable object reference," which is correct. However, as const doesn't provide reference stability—the const keyword already prevents reassignment of the binding. The as const assertion only makes the object and its properties deeply readonly and narrows types to literals.

While not harmful (and potentially beneficial for immutability), it may be confusing to attribute reference stability to as const rather than const. Consider either removing as const or updating the comment to reflect that it provides readonly immutability rather than reference stability.

Alternative without `as const`
-// Module-level constant so React 19 receives a stable object reference for the
-// server snapshot. Creating this inside the hook would produce a new reference
-// on every render, causing an infinite loop during SSR/hydration.
-const SERVER_SNAPSHOT = { collection: null, version: 0 } as const
+// Module-level constant so React 19 receives a stable object reference for the
+// server snapshot. Creating this inside the hook would produce a new reference
+// on every render, causing an infinite loop during SSR/hydration.
+const SERVER_SNAPSHOT: { collection: null; version: number } = { 
+  collection: null, 
+  version: 0 
+}

This provides the same reference stability with a more explicit type annotation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react-db/src/useLiveQuery.ts` around lines 23 - 26, The comment
incorrectly attributes reference stability to the `as const` assertion on
SERVER_SNAPSHOT in useLiveQuery.ts; remove the misleading `as const` (i.e.,
declare SERVER_SNAPSHOT = { collection: null, version: 0 } as the module-level
const) or alternatively keep immutability but update the comment to say `as
const` provides deep readonly typing rather than reference stability; locate
SERVER_SNAPSHOT and either drop the `as const` assertion or adjust the comment
text accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/react-db/src/useLiveQuery.ts`:
- Around line 23-26: The comment incorrectly attributes reference stability to
the `as const` assertion on SERVER_SNAPSHOT in useLiveQuery.ts; remove the
misleading `as const` (i.e., declare SERVER_SNAPSHOT = { collection: null,
version: 0 } as the module-level const) or alternatively keep immutability but
update the comment to say `as const` provides deep readonly typing rather than
reference stability; locate SERVER_SNAPSHOT and either drop the `as const`
assertion or adjust the comment text accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d7eb07b3-f892-4abf-a303-98c32e42ea21

📥 Commits

Reviewing files that changed from the base of the PR and between 6d9e4c6 and ccfaa53.

📒 Files selected for processing (1)
  • packages/react-db/src/useLiveQuery.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant