Skip to content

Conversation

@schiller-manuel
Copy link
Contributor

@schiller-manuel schiller-manuel commented Dec 21, 2025

Summary by CodeRabbit

  • New Features

    • Enhanced SSR streaming with a richer serialization lifecycle, buffering/flush controls, and a new streaming test app with many demo routes.
  • Bug Fixes

    • Simplified HTML injection payloads (removed promise wrapper) for more predictable server output and safer injection timing.
  • Tests

    • Comprehensive end-to-end suites added for streaming, deferred data, concurrency, hydration, and client navigation.

✏️ Tip: You can customize this high-level summary in your review settings.

Benchmark Summary
| Scenario | Winner | Speed vs Regex |
|----------|--------|----------------|
| Small Chunk (~70 chars) | optimized (charCodeAt) | 5x faster |
| Medium Chunk (~1.5KB) | manual backwards scan | 52x faster |
| Large Chunk (~13KB) | optimized (charCodeAt) | 370x faster |
| Web Components | optimized (charCodeAt) | 75x faster |
| No Closing Tags | regex wins | 1x (regex is actually best here) |
| Nested React-like | optimized (charCodeAt) | 28x faster |
| Partial Chunk | optimized (charCodeAt) | 2x faster |
Key findings:
1. The optimized (charCodeAt) approach is consistently the fastest or close to fastest across all scenarios
2. For realistic HTML chunks with closing tags, the optimized approach is 5x to 370x faster than regex
3. The only case where regex wins is when there are no closing tags at all, which is an edge case for plain text - but even then, all approaches are blazingly fast (millions of ops/sec)
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 21, 2025

Caution

Review failed

The pull request is closed.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'fix: streaming' is too vague and generic, failing to convey the specific changes made in this comprehensive PR involving multiple streaming, SSR, and API changes. Replace with a more descriptive title that captures the main change, such as 'refactor: simplify SSR HTML buffering and streaming lifecycle' or 'fix: streamline streaming SSR API and buffer management'.
Docstring Coverage ⚠️ Warning Docstring coverage is 44.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c7d91b and b17bf72.

📒 Files selected for processing (2)
  • e2e/react-start/streaming-ssr/src/routes/concurrent.tsx (1 hunks)
  • packages/router-core/src/ssr/transformStreamWithRouter.ts (2 hunks)

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

@nx-cloud
Copy link

nx-cloud bot commented Dec 21, 2025

View your CI Pipeline Execution ↗ for commit b17bf72

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ✅ Succeeded 9m 32s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 1m 33s View ↗

☁️ Nx Cloud last updated this comment at 2025-12-21 23:24:18 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Dec 21, 2025

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@6175

@tanstack/directive-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/directive-functions-plugin@6175

@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/eslint-plugin-router@6175

@tanstack/history

npm i https://pkg.pr.new/TanStack/router/@tanstack/history@6175

@tanstack/nitro-v2-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/nitro-v2-vite-plugin@6175

@tanstack/react-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router@6175

@tanstack/react-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-devtools@6175

@tanstack/react-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-ssr-query@6175

@tanstack/react-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@6175

@tanstack/react-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@6175

@tanstack/react-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@6175

@tanstack/router-cli

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-cli@6175

@tanstack/router-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-core@6175

@tanstack/router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools@6175

@tanstack/router-devtools-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools-core@6175

@tanstack/router-generator

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-generator@6175

@tanstack/router-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-plugin@6175

@tanstack/router-ssr-query-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-ssr-query-core@6175

@tanstack/router-utils

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-utils@6175

@tanstack/router-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-vite-plugin@6175

@tanstack/server-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/server-functions-plugin@6175

@tanstack/solid-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router@6175

@tanstack/solid-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-devtools@6175

@tanstack/solid-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-ssr-query@6175

@tanstack/solid-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@6175

@tanstack/solid-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@6175

@tanstack/solid-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@6175

@tanstack/start-client-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@6175

@tanstack/start-plugin-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-plugin-core@6175

@tanstack/start-server-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@6175

@tanstack/start-static-server-functions

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-static-server-functions@6175

@tanstack/start-storage-context

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-storage-context@6175

@tanstack/valibot-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@6175

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@6175

@tanstack/vue-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router@6175

@tanstack/vue-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router-devtools@6175

@tanstack/vue-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router-ssr-query@6175

@tanstack/vue-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start@6175

@tanstack/vue-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start-client@6175

@tanstack/vue-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start-server@6175

@tanstack/zod-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@6175

commit: 9c7d91b

Copy link
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.

Actionable comments posted: 4

🧹 Nitpick comments (3)
e2e/react-start/streaming-ssr/package.json (1)

11-11: Consider cross-platform compatibility for the test script.

The rm -rf command in the test:e2e script may not work on Windows environments without Git Bash or similar Unix-like shell.

🔎 Alternative using rimraf or cross-platform approach

If cross-platform support is needed, consider using a package like rimraf or Node.js script:

-    "test:e2e": "rm -rf port*.txt; playwright test --project=chromium"
+    "test:e2e": "rimraf port*.txt && playwright test --project=chromium"

Or use a Node.js one-liner:

-    "test:e2e": "rm -rf port*.txt; playwright test --project=chromium"
+    "test:e2e": "node -e \"require('fs').rmSync('port*.txt',{force:true,recursive:true})\" && playwright test --project=chromium"
e2e/react-start/streaming-ssr/src/router.tsx (1)

6-18: Add explicit return type annotation.

Based on coding guidelines for TypeScript strict mode, the getRouter function should have an explicit return type annotation to improve type safety and make the API contract clear.

🔎 Proposed fix
+import type { Router } from '@tanstack/react-router'
+
 export function getRouter() {
+export function getRouter(): Router {
   const queryClient = new QueryClient()
   const router = createRouter({
     routeTree,
     context: { queryClient },
     scrollRestoration: true,
   })
   setupRouterSsrQueryIntegration({
     router,
     queryClient,
   })
   return router
 }
e2e/react-start/streaming-ssr/src/routes/query-heavy.tsx (1)

145-173: Consider extracting a shared display component.

SyncQueryDisplay and AsyncQueryDisplay have nearly identical implementations - only the type annotation differs. Since the return types have the same shape, you could consolidate into a single component.

🔎 Proposed refactor
-function SyncQueryDisplay({
-  queryOpts,
-  testId,
-}: {
-  queryOpts: typeof syncQuery1
-  testId: string
-}) {
-  const { data } = useSuspenseQuery(queryOpts)
-  return (
-    <div data-testid={testId}>
-      {data.value} (source: {data.source})
-    </div>
-  )
-}
-
-function AsyncQueryDisplay({
-  queryOpts,
-  testId,
-}: {
-  queryOpts: typeof fastAsyncQuery1
-  testId: string
-}) {
-  const { data } = useSuspenseQuery(queryOpts)
-  return (
-    <div data-testid={testId}>
-      {data.value} (source: {data.source})
-    </div>
-  )
-}
+function QueryDisplay({
+  queryOpts,
+  testId,
+}: {
+  queryOpts: typeof syncQuery1 | typeof fastAsyncQuery1
+  testId: string
+}) {
+  const { data } = useSuspenseQuery(queryOpts)
+  return (
+    <div data-testid={testId}>
+      {data.value} (source: {data.source})
+    </div>
+  )
+}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8d9759 and e224b6a.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (39)
  • docs/router/framework/react/api/router/RouterEventsType.md (0 hunks)
  • e2e/react-start/streaming-ssr/.gitignore (1 hunks)
  • e2e/react-start/streaming-ssr/package.json (1 hunks)
  • e2e/react-start/streaming-ssr/playwright.config.ts (1 hunks)
  • e2e/react-start/streaming-ssr/src/routeTree.gen.ts (1 hunks)
  • e2e/react-start/streaming-ssr/src/router.tsx (1 hunks)
  • e2e/react-start/streaming-ssr/src/routes/__root.tsx (1 hunks)
  • e2e/react-start/streaming-ssr/src/routes/concurrent.tsx (1 hunks)
  • e2e/react-start/streaming-ssr/src/routes/deferred.tsx (1 hunks)
  • e2e/react-start/streaming-ssr/src/routes/fast-serial.tsx (1 hunks)
  • e2e/react-start/streaming-ssr/src/routes/index.tsx (1 hunks)
  • e2e/react-start/streaming-ssr/src/routes/many-promises.tsx (1 hunks)
  • e2e/react-start/streaming-ssr/src/routes/nested-deferred.tsx (1 hunks)
  • e2e/react-start/streaming-ssr/src/routes/query-heavy.tsx (1 hunks)
  • e2e/react-start/streaming-ssr/src/routes/slow-render.tsx (1 hunks)
  • e2e/react-start/streaming-ssr/src/routes/stream.tsx (1 hunks)
  • e2e/react-start/streaming-ssr/src/routes/sync-only.tsx (1 hunks)
  • e2e/react-start/streaming-ssr/tests/client-navigation.spec.ts (1 hunks)
  • e2e/react-start/streaming-ssr/tests/concurrent.spec.ts (1 hunks)
  • e2e/react-start/streaming-ssr/tests/deferred.spec.ts (1 hunks)
  • e2e/react-start/streaming-ssr/tests/fast-serial.spec.ts (1 hunks)
  • e2e/react-start/streaming-ssr/tests/fixtures.ts (1 hunks)
  • e2e/react-start/streaming-ssr/tests/home.spec.ts (1 hunks)
  • e2e/react-start/streaming-ssr/tests/many-promises.spec.ts (1 hunks)
  • e2e/react-start/streaming-ssr/tests/nested-deferred.spec.ts (1 hunks)
  • e2e/react-start/streaming-ssr/tests/query-heavy.spec.ts (1 hunks)
  • e2e/react-start/streaming-ssr/tests/slow-render.spec.ts (1 hunks)
  • e2e/react-start/streaming-ssr/tests/stream.spec.ts (1 hunks)
  • e2e/react-start/streaming-ssr/tests/sync-only.spec.ts (1 hunks)
  • e2e/react-start/streaming-ssr/tsconfig.json (1 hunks)
  • e2e/react-start/streaming-ssr/vite.config.ts (1 hunks)
  • packages/react-router/src/ssr/renderRouterToString.tsx (1 hunks)
  • packages/router-core/src/router.ts (1 hunks)
  • packages/router-core/src/ssr/constants.ts (1 hunks)
  • packages/router-core/src/ssr/ssr-server.ts (7 hunks)
  • packages/router-core/src/ssr/transformStreamWithRouter.ts (2 hunks)
  • packages/router-core/tests/closing-tag-detection.bench.ts (1 hunks)
  • packages/solid-router/src/ssr/renderRouterToString.tsx (1 hunks)
  • packages/vue-router/src/ssr/renderRouterToString.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • docs/router/framework/react/api/router/RouterEventsType.md
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript strict mode with extensive type safety for all code

Files:

  • e2e/react-start/streaming-ssr/src/routes/fast-serial.tsx
  • e2e/react-start/streaming-ssr/src/routes/concurrent.tsx
  • e2e/react-start/streaming-ssr/tests/fast-serial.spec.ts
  • e2e/react-start/streaming-ssr/vite.config.ts
  • packages/vue-router/src/ssr/renderRouterToString.tsx
  • packages/router-core/tests/closing-tag-detection.bench.ts
  • e2e/react-start/streaming-ssr/tests/many-promises.spec.ts
  • e2e/react-start/streaming-ssr/tests/slow-render.spec.ts
  • e2e/react-start/streaming-ssr/src/routes/slow-render.tsx
  • e2e/react-start/streaming-ssr/tests/client-navigation.spec.ts
  • e2e/react-start/streaming-ssr/tests/concurrent.spec.ts
  • packages/react-router/src/ssr/renderRouterToString.tsx
  • e2e/react-start/streaming-ssr/tests/sync-only.spec.ts
  • packages/router-core/src/router.ts
  • e2e/react-start/streaming-ssr/src/routes/index.tsx
  • e2e/react-start/streaming-ssr/src/routes/__root.tsx
  • e2e/react-start/streaming-ssr/src/routes/many-promises.tsx
  • e2e/react-start/streaming-ssr/tests/home.spec.ts
  • e2e/react-start/streaming-ssr/tests/query-heavy.spec.ts
  • e2e/react-start/streaming-ssr/src/routes/stream.tsx
  • packages/router-core/src/ssr/constants.ts
  • e2e/react-start/streaming-ssr/src/routes/nested-deferred.tsx
  • packages/solid-router/src/ssr/renderRouterToString.tsx
  • e2e/react-start/streaming-ssr/playwright.config.ts
  • e2e/react-start/streaming-ssr/tests/nested-deferred.spec.ts
  • e2e/react-start/streaming-ssr/tests/deferred.spec.ts
  • e2e/react-start/streaming-ssr/src/router.tsx
  • e2e/react-start/streaming-ssr/src/routes/query-heavy.tsx
  • e2e/react-start/streaming-ssr/tests/stream.spec.ts
  • e2e/react-start/streaming-ssr/tests/fixtures.ts
  • e2e/react-start/streaming-ssr/src/routeTree.gen.ts
  • packages/router-core/src/ssr/transformStreamWithRouter.ts
  • e2e/react-start/streaming-ssr/src/routes/deferred.tsx
  • packages/router-core/src/ssr/ssr-server.ts
  • e2e/react-start/streaming-ssr/src/routes/sync-only.tsx
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Implement ESLint rules for router best practices using the ESLint plugin router

Files:

  • e2e/react-start/streaming-ssr/src/routes/fast-serial.tsx
  • e2e/react-start/streaming-ssr/src/routes/concurrent.tsx
  • e2e/react-start/streaming-ssr/tests/fast-serial.spec.ts
  • e2e/react-start/streaming-ssr/vite.config.ts
  • packages/vue-router/src/ssr/renderRouterToString.tsx
  • packages/router-core/tests/closing-tag-detection.bench.ts
  • e2e/react-start/streaming-ssr/tests/many-promises.spec.ts
  • e2e/react-start/streaming-ssr/tests/slow-render.spec.ts
  • e2e/react-start/streaming-ssr/src/routes/slow-render.tsx
  • e2e/react-start/streaming-ssr/tests/client-navigation.spec.ts
  • e2e/react-start/streaming-ssr/tests/concurrent.spec.ts
  • packages/react-router/src/ssr/renderRouterToString.tsx
  • e2e/react-start/streaming-ssr/tests/sync-only.spec.ts
  • packages/router-core/src/router.ts
  • e2e/react-start/streaming-ssr/src/routes/index.tsx
  • e2e/react-start/streaming-ssr/src/routes/__root.tsx
  • e2e/react-start/streaming-ssr/src/routes/many-promises.tsx
  • e2e/react-start/streaming-ssr/tests/home.spec.ts
  • e2e/react-start/streaming-ssr/tests/query-heavy.spec.ts
  • e2e/react-start/streaming-ssr/src/routes/stream.tsx
  • packages/router-core/src/ssr/constants.ts
  • e2e/react-start/streaming-ssr/src/routes/nested-deferred.tsx
  • packages/solid-router/src/ssr/renderRouterToString.tsx
  • e2e/react-start/streaming-ssr/playwright.config.ts
  • e2e/react-start/streaming-ssr/tests/nested-deferred.spec.ts
  • e2e/react-start/streaming-ssr/tests/deferred.spec.ts
  • e2e/react-start/streaming-ssr/src/router.tsx
  • e2e/react-start/streaming-ssr/src/routes/query-heavy.tsx
  • e2e/react-start/streaming-ssr/tests/stream.spec.ts
  • e2e/react-start/streaming-ssr/tests/fixtures.ts
  • e2e/react-start/streaming-ssr/src/routeTree.gen.ts
  • packages/router-core/src/ssr/transformStreamWithRouter.ts
  • e2e/react-start/streaming-ssr/src/routes/deferred.tsx
  • packages/router-core/src/ssr/ssr-server.ts
  • e2e/react-start/streaming-ssr/src/routes/sync-only.tsx
**/package.json

📄 CodeRabbit inference engine (AGENTS.md)

Use workspace protocol workspace:* for internal dependencies in package.json files

Files:

  • e2e/react-start/streaming-ssr/package.json
🧠 Learnings (10)
📚 Learning: 2025-10-08T08:11:47.088Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5402
File: packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts:19-21
Timestamp: 2025-10-08T08:11:47.088Z
Learning: Test snapshot files in the router-generator tests directory (e.g., files matching the pattern `packages/router-generator/tests/generator/**/routeTree*.snapshot.ts` or `routeTree*.snapshot.js`) should not be modified or have issues flagged, as they are fixtures used to verify the generator's output and are intentionally preserved as-is.

Applied to files:

  • packages/router-core/tests/closing-tag-detection.bench.ts
  • e2e/react-start/streaming-ssr/tests/client-navigation.spec.ts
  • e2e/react-start/streaming-ssr/tests/sync-only.spec.ts
  • e2e/react-start/streaming-ssr/src/routes/index.tsx
  • e2e/react-start/streaming-ssr/src/routes/__root.tsx
  • e2e/react-start/streaming-ssr/tests/home.spec.ts
  • e2e/react-start/streaming-ssr/tests/query-heavy.spec.ts
  • e2e/react-start/streaming-ssr/src/routes/nested-deferred.tsx
  • e2e/react-start/streaming-ssr/src/routes/query-heavy.tsx
  • e2e/react-start/streaming-ssr/src/routeTree.gen.ts
  • e2e/react-start/streaming-ssr/src/routes/sync-only.tsx
📚 Learning: 2025-12-06T15:03:07.223Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T15:03:07.223Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript strict mode with extensive type safety for all code

Applied to files:

  • e2e/react-start/streaming-ssr/tsconfig.json
📚 Learning: 2025-11-02T16:16:24.898Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5732
File: packages/start-client-core/src/client/hydrateStart.ts:6-9
Timestamp: 2025-11-02T16:16:24.898Z
Learning: In packages/start-client-core/src/client/hydrateStart.ts, the `import/no-duplicates` ESLint disable is necessary for imports from `#tanstack-router-entry` and `#tanstack-start-entry` because both aliases resolve to the same placeholder file (`fake-start-entry.js`) in package.json during static analysis, even though they resolve to different files at runtime.

Applied to files:

  • e2e/react-start/streaming-ssr/tsconfig.json
  • e2e/react-start/streaming-ssr/tests/sync-only.spec.ts
  • e2e/react-start/streaming-ssr/src/routes/__root.tsx
  • e2e/react-start/streaming-ssr/package.json
  • e2e/react-start/streaming-ssr/src/routeTree.gen.ts
  • e2e/react-start/streaming-ssr/src/routes/deferred.tsx
  • e2e/react-start/streaming-ssr/src/routes/sync-only.tsx
📚 Learning: 2025-12-06T15:03:07.223Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T15:03:07.223Z
Learning: Applies to **/*.{js,ts,tsx} : Implement ESLint rules for router best practices using the ESLint plugin router

Applied to files:

  • e2e/react-start/streaming-ssr/tsconfig.json
  • e2e/react-start/streaming-ssr/src/routes/index.tsx
  • e2e/react-start/streaming-ssr/src/router.tsx
  • e2e/react-start/streaming-ssr/src/routeTree.gen.ts
📚 Learning: 2025-12-06T15:03:07.223Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T15:03:07.223Z
Learning: Use file-based routing in `src/routes/` directories or code-based routing with route definitions

Applied to files:

  • e2e/react-start/streaming-ssr/src/routes/index.tsx
  • e2e/react-start/streaming-ssr/src/routeTree.gen.ts
  • e2e/react-start/streaming-ssr/src/routes/sync-only.tsx
📚 Learning: 2025-12-21T12:52:27.078Z
Learnt from: Sheraff
Repo: TanStack/router PR: 6171
File: packages/router-core/src/new-process-route-tree.ts:898-898
Timestamp: 2025-12-21T12:52:27.078Z
Learning: In `packages/router-core/src/new-process-route-tree.ts`, the matching logic intentionally allows paths without trailing slashes to match index routes with trailing slashes (e.g., `/a` can match `/a/` route), but not vice-versa (e.g., `/a/` cannot match `/a` layout route). This is implemented via the condition `!pathIsIndex || node.kind === SEGMENT_TYPE_INDEX` and is a deliberate design decision to provide better UX by being permissive with missing trailing slashes.

Applied to files:

  • e2e/react-start/streaming-ssr/src/routes/index.tsx
  • e2e/react-start/streaming-ssr/src/routeTree.gen.ts
📚 Learning: 2025-12-17T02:17:55.086Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 6120
File: packages/router-generator/src/generator.ts:654-657
Timestamp: 2025-12-17T02:17:55.086Z
Learning: In `packages/router-generator/src/generator.ts`, pathless_layout routes must receive a `path` property when they have a `cleanedPath`, even though they are non-path routes. This is necessary because child routes inherit the path from their parent, and without this property, child routes would not have the correct full path at runtime.

Applied to files:

  • e2e/react-start/streaming-ssr/src/routes/index.tsx
  • e2e/react-start/streaming-ssr/src/routes/__root.tsx
  • e2e/react-start/streaming-ssr/src/routeTree.gen.ts
📚 Learning: 2025-10-01T18:30:26.591Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 5330
File: packages/router-core/src/router.ts:2231-2245
Timestamp: 2025-10-01T18:30:26.591Z
Learning: In `packages/router-core/src/router.ts`, the `resolveRedirect` method intentionally strips the router's origin from redirect URLs when they match (e.g., `https://foo.com/bar` → `/bar` for same-origin redirects) while preserving the full URL for cross-origin redirects. This logic should not be removed or simplified to use `location.publicHref` directly.

Applied to files:

  • packages/solid-router/src/ssr/renderRouterToString.tsx
  • packages/router-core/src/ssr/transformStreamWithRouter.ts
📚 Learning: 2025-10-09T12:59:14.842Z
Learnt from: hokkyss
Repo: TanStack/router PR: 5418
File: e2e/react-start/custom-identifier-prefix/public/site.webmanifest:2-3
Timestamp: 2025-10-09T12:59:14.842Z
Learning: In e2e test fixtures (files under e2e directories), empty or placeholder values in configuration files like site.webmanifest are acceptable and should not be flagged unless the test specifically validates those fields.

Applied to files:

  • e2e/react-start/streaming-ssr/playwright.config.ts
  • e2e/react-start/streaming-ssr/tests/fixtures.ts
📚 Learning: 2025-10-01T18:31:35.420Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 5330
File: e2e/react-start/custom-basepath/src/routeTree.gen.ts:58-61
Timestamp: 2025-10-01T18:31:35.420Z
Learning: Do not review files named `routeTree.gen.ts` in TanStack Router repositories, as these are autogenerated files that should not be manually modified.

Applied to files:

  • e2e/react-start/streaming-ssr/src/routeTree.gen.ts
🧬 Code graph analysis (23)
e2e/react-start/streaming-ssr/src/routes/fast-serial.tsx (4)
e2e/react-start/streaming-ssr/src/routes/deferred.tsx (1)
  • Route (30-56)
e2e/react-start/streaming-ssr/src/routes/many-promises.tsx (1)
  • Route (14-46)
e2e/react-start/streaming-ssr/src/routes/slow-render.tsx (1)
  • Route (25-48)
e2e/react-start/streaming-ssr/src/routes/sync-only.tsx (1)
  • Route (8-21)
e2e/react-start/streaming-ssr/src/routes/concurrent.tsx (6)
e2e/react-start/streaming-ssr/src/routes/__root.tsx (1)
  • Route (11-29)
e2e/react-start/streaming-ssr/src/routes/deferred.tsx (1)
  • Route (30-56)
e2e/react-start/streaming-ssr/src/routes/index.tsx (1)
  • Route (3-5)
e2e/react-start/streaming-ssr/src/routes/many-promises.tsx (1)
  • Route (14-46)
e2e/react-start/streaming-ssr/src/routes/stream.tsx (1)
  • Route (4-24)
e2e/react-start/streaming-ssr/src/routes/sync-only.tsx (1)
  • Route (8-21)
e2e/react-start/streaming-ssr/tests/fast-serial.spec.ts (1)
e2e/react-start/streaming-ssr/tests/fixtures.ts (3)
  • test (47-74)
  • expect (90-90)
  • testWithHydration (80-87)
e2e/react-start/streaming-ssr/tests/many-promises.spec.ts (1)
e2e/react-start/streaming-ssr/tests/fixtures.ts (3)
  • test (47-74)
  • expect (90-90)
  • testWithHydration (80-87)
e2e/react-start/streaming-ssr/tests/slow-render.spec.ts (1)
e2e/react-start/streaming-ssr/tests/fixtures.ts (3)
  • test (47-74)
  • expect (90-90)
  • testWithHydration (80-87)
e2e/react-start/streaming-ssr/src/routes/slow-render.tsx (3)
e2e/react-start/streaming-ssr/src/routes/deferred.tsx (1)
  • Route (30-56)
e2e/react-start/streaming-ssr/src/routes/stream.tsx (1)
  • Route (4-24)
e2e/react-start/streaming-ssr/src/routes/sync-only.tsx (1)
  • Route (8-21)
e2e/react-start/streaming-ssr/tests/client-navigation.spec.ts (1)
e2e/react-start/streaming-ssr/tests/fixtures.ts (3)
  • test (47-74)
  • expect (90-90)
  • testWithHydration (80-87)
e2e/react-start/streaming-ssr/tests/concurrent.spec.ts (1)
e2e/react-start/streaming-ssr/tests/fixtures.ts (3)
  • test (47-74)
  • expect (90-90)
  • testWithHydration (80-87)
e2e/react-start/streaming-ssr/tests/sync-only.spec.ts (1)
e2e/react-start/streaming-ssr/tests/fixtures.ts (3)
  • test (47-74)
  • expect (90-90)
  • testWithHydration (80-87)
packages/router-core/src/router.ts (3)
packages/react-router/src/index.tsx (1)
  • RouterManagedTag (78-78)
packages/router-core/src/index.ts (1)
  • RouterManagedTag (73-73)
packages/solid-router/src/index.tsx (1)
  • RouterManagedTag (77-77)
e2e/react-start/streaming-ssr/src/routes/index.tsx (9)
e2e/react-start/streaming-ssr/src/routes/__root.tsx (1)
  • Route (11-29)
e2e/react-start/streaming-ssr/src/routes/concurrent.tsx (1)
  • Route (27-53)
e2e/react-start/streaming-ssr/src/routes/deferred.tsx (1)
  • Route (30-56)
e2e/react-start/streaming-ssr/src/routes/fast-serial.tsx (1)
  • Route (14-27)
e2e/react-start/streaming-ssr/src/routes/nested-deferred.tsx (1)
  • Route (21-35)
e2e/react-start/streaming-ssr/src/routes/query-heavy.tsx (1)
  • Route (140-142)
e2e/react-start/streaming-ssr/src/routes/slow-render.tsx (1)
  • Route (25-48)
e2e/react-start/streaming-ssr/src/routes/stream.tsx (1)
  • Route (4-24)
e2e/react-start/streaming-ssr/src/routes/sync-only.tsx (1)
  • Route (8-21)
e2e/react-start/streaming-ssr/src/routes/__root.tsx (2)
e2e/react-start/streaming-ssr/src/routes/index.tsx (1)
  • Route (3-5)
e2e/react-start/streaming-ssr/src/routes/stream.tsx (1)
  • Route (4-24)
e2e/react-start/streaming-ssr/src/routes/many-promises.tsx (4)
e2e/react-start/streaming-ssr/src/routes/concurrent.tsx (1)
  • Route (27-53)
e2e/react-start/streaming-ssr/src/routes/deferred.tsx (1)
  • Route (30-56)
e2e/react-start/streaming-ssr/src/routes/slow-render.tsx (1)
  • Route (25-48)
e2e/react-start/streaming-ssr/src/routes/stream.tsx (1)
  • Route (4-24)
e2e/react-start/streaming-ssr/tests/home.spec.ts (1)
e2e/react-start/streaming-ssr/tests/fixtures.ts (3)
  • test (47-74)
  • testWithHydration (80-87)
  • expect (90-90)
e2e/react-start/streaming-ssr/tests/query-heavy.spec.ts (1)
e2e/react-start/streaming-ssr/tests/fixtures.ts (3)
  • test (47-74)
  • expect (90-90)
  • testWithHydration (80-87)
e2e/react-start/streaming-ssr/src/routes/stream.tsx (3)
e2e/react-start/streaming-ssr/src/routes/index.tsx (1)
  • Route (3-5)
e2e/react-start/streaming-ssr/src/routes/many-promises.tsx (1)
  • Route (14-46)
e2e/react-start/streaming-ssr/src/routes/slow-render.tsx (1)
  • Route (25-48)
e2e/react-start/streaming-ssr/src/routes/nested-deferred.tsx (3)
e2e/react-start/streaming-ssr/src/routes/deferred.tsx (1)
  • Route (30-56)
e2e/react-start/streaming-ssr/src/routes/many-promises.tsx (1)
  • Route (14-46)
e2e/react-start/streaming-ssr/src/routes/stream.tsx (1)
  • Route (4-24)
e2e/react-start/streaming-ssr/tests/nested-deferred.spec.ts (1)
e2e/react-start/streaming-ssr/tests/fixtures.ts (3)
  • test (47-74)
  • expect (90-90)
  • testWithHydration (80-87)
e2e/react-start/streaming-ssr/tests/deferred.spec.ts (1)
e2e/react-start/streaming-ssr/tests/fixtures.ts (3)
  • test (47-74)
  • expect (90-90)
  • testWithHydration (80-87)
e2e/react-start/streaming-ssr/src/routes/query-heavy.tsx (4)
e2e/react-start/streaming-ssr/src/routes/__root.tsx (1)
  • Route (11-29)
e2e/react-start/streaming-ssr/src/routes/index.tsx (1)
  • Route (3-5)
e2e/react-start/streaming-ssr/src/routes/many-promises.tsx (1)
  • Route (14-46)
e2e/react-start/streaming-ssr/src/routes/slow-render.tsx (1)
  • Route (25-48)
e2e/react-start/streaming-ssr/tests/stream.spec.ts (1)
e2e/react-start/streaming-ssr/tests/fixtures.ts (3)
  • test (47-74)
  • testWithHydration (80-87)
  • expect (90-90)
e2e/react-start/streaming-ssr/src/routes/deferred.tsx (4)
e2e/react-start/streaming-ssr/src/routes/fast-serial.tsx (1)
  • Route (14-27)
e2e/react-start/streaming-ssr/src/routes/index.tsx (1)
  • Route (3-5)
e2e/react-start/streaming-ssr/src/routes/many-promises.tsx (1)
  • Route (14-46)
e2e/react-start/streaming-ssr/src/routes/nested-deferred.tsx (1)
  • Route (21-35)
packages/router-core/src/ssr/ssr-server.ts (2)
packages/router-core/src/router.ts (1)
  • AnyRouter (782-782)
packages/router-core/src/index.ts (1)
  • defaultSerovalPlugins (433-433)
🪛 Biome (2.1.2)
e2e/react-start/streaming-ssr/src/routes/concurrent.tsx

[error] 68-68: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

e2e/react-start/streaming-ssr/src/routes/slow-render.tsx

[error] 71-71: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

e2e/react-start/streaming-ssr/src/routes/many-promises.tsx

[error] 63-63: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

e2e/react-start/streaming-ssr/src/routes/stream.tsx

[error] 96-96: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

e2e/react-start/streaming-ssr/src/routes/nested-deferred.tsx

[error] 58-58: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)


[error] 88-88: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)


[error] 115-115: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)


[error] 126-126: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

e2e/react-start/streaming-ssr/src/routes/deferred.tsx

[error] 83-83: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)


[error] 99-99: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test
  • GitHub Check: Preview
🔇 Additional comments (60)
e2e/react-start/streaming-ssr/.gitignore (1)

1-19: Solid gitignore coverage for the e2e streaming SSR project.

The patterns comprehensively cover package manager artifacts, OS files, environment/secrets, build outputs, and Playwright-specific test artifacts. All entries are syntactically correct.

Minor suggestion: Consider extending this with IDE-specific directories (e.g., .vscode/, .idea/) and log files (e.g., *.log) if not already covered at the repository root, but the current set is suitable for the project's core needs.

e2e/react-start/streaming-ssr/vite.config.ts (1)

1-9: LGTM! Clean e2e test configuration.

The Vite configuration is well-structured and appropriate for the streaming SSR e2e test suite. Using defineConfig provides type safety, and the minimal setup with the TanStack Start plugin and fixed port 3000 is standard practice for controlled test environments.

e2e/react-start/streaming-ssr/package.json (1)

1-30: Overall structure looks good.

The package configuration is appropriate for an e2e streaming SSR test setup with React 19, Playwright, and necessary TanStack dependencies.

e2e/react-start/streaming-ssr/tsconfig.json (1)

1-22: LGTM! Modern, strict TypeScript configuration.

The configuration follows TypeScript 5.0+ best practices with "Bundler" moduleResolution, modern JSX transform, and strict type checking enabled. The "react-jsx" option is compatible with React 17 RC and higher, supporting the TanStack Router requirement for React 18.x or 19.x. All settings are well-suited for a bundler-based e2e test project.

packages/vue-router/src/ssr/renderRouterToString.tsx (1)

20-24: LGTM! Clean refactor to buffered HTML injection.

The synchronous buffer retrieval via takeBufferedHtml() with a conditional check is a clean improvement over the previous async approach. The replacement logic correctly injects HTML before the closing </body> tag.

e2e/react-start/streaming-ssr/playwright.config.ts (1)

11-20: LGTM! Appropriate test configuration.

Single worker execution is the right choice for SSR streaming tests to prevent port conflicts and ensure deterministic test execution.

packages/router-core/tests/closing-tag-detection.bench.ts (3)

1-27: LGTM! Well-documented baseline implementation.

The regex-based implementation serves as a good baseline with clear documentation of the closing tag requirements. The pattern correctly matches HTML closing tags including web components with colons, dots, and hyphens.


258-297: Excellent! Verification ensures benchmark validity.

Running verification at module load time ensures all implementations produce identical results before benchmarking begins. This is a best practice for comparative benchmarks.


299-441: LGTM! Comprehensive benchmark coverage.

The benchmark suite covers a wide range of realistic scenarios including small/medium/large chunks, web components, edge cases (no tags, partial chunks), and nested structures. This will provide valuable performance insights for choosing the optimal implementation.

e2e/react-start/streaming-ssr/src/routes/nested-deferred.tsx (2)

38-97: LGTM! Nested deferred rendering structure is correct.

The nested Suspense/Await pattern correctly demonstrates progressive data resolution. The static analysis warnings about children prop usage are false positives—TanStack Router's Await component intentionally accepts children as a prop for render props pattern, which is the documented API.


99-137: LGTM! Well-structured test case for nested deferred data.

The component effectively demonstrates multiple concurrent and nested deferred promises with appropriate Suspense boundaries and loading states. This will be valuable for testing SSR streaming behavior.

e2e/react-start/streaming-ssr/tests/fixtures.ts (3)

1-34: LGTM! Robust hydration verification.

The retry mechanism elegantly handles the race condition where SSR renders the button before React hydrates. The 10-second timeout is appropriate for streaming SSR tests.


36-74: LGTM! Effective error monitoring pattern.

The console error monitoring with whitelisting provides a good balance between catching unexpected errors and allowing known/expected error patterns. This will help catch issues early in SSR streaming tests.


76-90: LGTM! Clean fixture composition.

The automatic hydration verification at test end reduces boilerplate and ensures consistent hydration testing across the suite.

e2e/react-start/streaming-ssr/src/routes/stream.tsx (3)

1-27: LGTM! Clean route definition with streaming data.

The route effectively demonstrates both promise-based and stream-based data loading patterns, which are key scenarios for SSR streaming tests.


28-84: LGTM! Comprehensive stream handling with proper guards.

The effect correctly handles multiple edge cases:

  • Prevents re-reading the same stream
  • Resets state for new streams
  • Checks if stream is already locked
  • Properly cleans up the reader on unmount

The error handling appropriately ignores cancellation errors while logging other issues. This implementation should work correctly with React strict mode and hot module reloading.


86-116: LGTM! Clear progressive rendering of stream data.

The component effectively demonstrates both promise and stream data handling. The static analysis warning about the children prop is a false positive—this is TanStack Router's documented API for the Await component.

packages/router-core/src/ssr/constants.ts (1)

3-3: LGTM! Good practice to centralize SSR constants.

Exporting the stream barrier ID from a central constants file improves maintainability and aligns with the PR's broader SSR buffering refactor.

e2e/react-start/streaming-ssr/tests/home.spec.ts (2)

3-21: LGTM! Standard e2e test for page rendering.

The test appropriately waits for network idle before asserting on page content, and the assertions verify key navigation elements are present.


23-34: LGTM! Clean navigation test.

Using data-testid for link selection is more reliable than text-based selection, and the URL assertion confirms successful navigation.

packages/react-router/src/ssr/renderRouterToString.tsx (1)

17-21: LGTM! Simplified HTML injection with buffering.

The refactor from Promise-based aggregation to synchronous takeBufferedHtml() is clean and improves code clarity. The null check properly short-circuits injection when no buffered HTML exists.

e2e/react-start/streaming-ssr/src/routes/index.tsx (1)

1-52: LGTM! Well-structured test navigation page.

The root route provides clear navigation to all test scenarios with consistent test IDs for e2e assertions. The component structure and routing setup are correct.

e2e/react-start/streaming-ssr/tests/client-navigation.spec.ts (3)

50-74: Excellent test coverage for ReadableStream locking fix.

This test specifically validates that navigating away from a streaming route before completion doesn't cause locking errors. The comment clearly explains the intent, and the fixture automatically monitors console errors.


100-137: Good validation of stream reusability.

This test ensures that returning to a streaming route creates a fresh stream without errors, which is important for proper resource cleanup and reinitialization.


302-372: Comprehensive hydration verification after navigation.

The testWithHydration fixture automatically verifies hydration at the end of each test, ensuring interactive elements work correctly after client-side navigation. Good use of test infrastructure.

packages/solid-router/src/ssr/renderRouterToString.tsx (1)

29-33: LGTM! Consistent buffering pattern across frameworks.

This implementation matches the React Router approach, providing a unified buffering strategy across the SSR stack. The logic is correct and consistent.

e2e/react-start/streaming-ssr/tests/many-promises.spec.ts (1)

3-93: LGTM! Comprehensive testing of concurrent promise resolution.

The test suite thoroughly validates progressive resolution across 15 promises with varying delays, ensuring proper streaming behavior and ordering. The grouping by timing categories makes the intent clear.

e2e/react-start/streaming-ssr/src/routes/fast-serial.tsx (2)

5-12: LGTM! Server function correctly defined.

The server function uses appropriate patterns with const assertion for the source field, ensuring type safety.


14-53: LGTM! Well-structured test route.

The route correctly implements fast serialization testing with immediate data resolution and comprehensive source tracking for SSR validation.

e2e/react-start/streaming-ssr/tests/sync-only.spec.ts (2)

10-28: LGTM! Solid validation of synchronous SSR.

The test correctly verifies that loader data originates from the server, confirming SSR streaming works for synchronous scenarios.


45-67: Good validation of SSR bootstrap scripts.

The HTML inspection test ensures that dehydrated router state ($_TSR) and serialization markers are present in the initial response, which is crucial for proper hydration.

e2e/react-start/streaming-ssr/tests/stream.spec.ts (1)

1-46: LGTM!

Well-structured test suite covering the key streaming SSR scenarios:

  • Promise resolution verification
  • Incremental stream chunk arrival
  • Client-side navigation with stream completion

The timeouts are appropriate for the async nature of the streaming tests, and the use of testWithHydration ensures hydration is verified automatically.

e2e/react-start/streaming-ssr/src/routes/concurrent.tsx (1)

55-72: LGTM - PromiseItem component structure is correct.

The children prop usage on Await is the documented render-prop API for @tanstack/react-router. The Biome lint warning is a false positive in this context since Await is designed to receive a function child to access the resolved value.

e2e/react-start/streaming-ssr/tests/fast-serial.spec.ts (1)

1-55: LGTM!

Comprehensive test coverage for fast serialization scenarios. The tests appropriately verify:

  • Data availability and server source indicators
  • Hydration integrity
  • Both SSR direct navigation and client-side navigation paths
e2e/react-start/streaming-ssr/tests/nested-deferred.spec.ts (1)

1-77: LGTM!

Good test coverage for nested deferred data scenarios. The tests verify:

  • All levels eventually resolve with appropriate timeouts
  • Loading states during data fetching
  • Hydration compatibility
  • Client-side navigation behavior
e2e/react-start/streaming-ssr/tests/slow-render.spec.ts (1)

1-73: LGTM!

Well-structured test suite that thoroughly verifies slow render behavior. The tests effectively prove SSR streaming works by checking that all data sources (quick, loader, deferred) originate from the server.

e2e/react-start/streaming-ssr/src/routes/many-promises.tsx (2)

14-46: LGTM - Promises created correctly inside loader.

Unlike the concurrent.tsx route, this correctly creates promises inside the loader function, ensuring each request gets fresh promise instances. This is the proper pattern for SSR streaming.


48-71: LGTM - PromiseItem component.

The children prop on Await is the documented render-prop API. The Biome lint warning is a false positive for this TanStack Router pattern.

e2e/react-start/streaming-ssr/src/routes/__root.tsx (1)

1-113: LGTM!

Well-structured root layout with:

  • Proper meta configuration for SSR
  • HydrationCheck component enabling programmatic hydration verification in tests
  • Complete navigation to all streaming SSR test routes
  • Correct placement of <Scripts /> at the end of the body
e2e/react-start/streaming-ssr/src/routes/slow-render.tsx (2)

16-23: Intentional blocking loop for test purposes - verified.

The synchronous blocking loop is intentional to simulate slow component rendering. This is appropriate for a test fixture but should never be used in production code. The 100ms delay per component (300ms total for 3 components) effectively tests the "render takes longer than serialization" scenario.


69-77: LGTM - Await with render prop.

The children prop usage on Await follows the documented TanStack Router API for accessing resolved promise data. The Biome lint warning is a false positive.

e2e/react-start/streaming-ssr/src/routes/sync-only.tsx (1)

1-41: LGTM!

The route implementation is clean and follows TanStack Router conventions. The loader correctly demonstrates synchronous data fetching behavior for SSR testing. The component properly consumes loader data via Route.useLoaderData() and renders with appropriate test IDs for e2e verification.

e2e/react-start/streaming-ssr/tests/concurrent.spec.ts (1)

1-100: LGTM!

The test suite provides good coverage for concurrent promise resolution scenarios. The use of testWithHydration fixture ensures automatic hydration verification. Timeout values are appropriately scaled for the batch delays, and the tests verify both ordering behavior and hydration compatibility.

e2e/react-start/streaming-ssr/tests/deferred.spec.ts (1)

1-120: LGTM!

Comprehensive test coverage for deferred data streaming. The tests effectively validate immediate vs. deferred rendering, loading states, hydration behavior, client-side navigation, and SSR correctness by verifying all data sources originate from the server.

e2e/react-start/streaming-ssr/src/routes/query-heavy.tsx (1)

1-143: LGTM!

Well-structured test route that effectively exercises SSR query streaming with mixed sync/async query patterns. The query definitions properly use staleTime: Infinity to prevent refetching, and the source tracking pattern is consistent with other routes in the test suite.

packages/router-core/src/ssr/transformStreamWithRouter.ts (5)

45-99: LGTM! Well-optimized closing tag finder.

The findLastClosingTagEnd function uses efficient charCodeAt-based parsing that avoids regex overhead. The implementation correctly validates HTML closing tag structure (</[a-zA-Z][\w:.-]*>) and handles edge cases appropriately.


129-161: Solid cleanup implementation with proper guards.

The cleanup function correctly handles multiple call prevention, safely unsubscribes listeners with try-catch protection, clears all timeouts, frees buffer memory, and delegates to router SSR cleanup. Good defensive programming.


165-202: Safe stream operations prevent double-close errors.

The safeEnqueue, safeClose, and safeError helpers properly guard against operations on closed streams. The empty catch blocks are appropriate here since the stream may already be in an error/closed state from consumer actions.


251-262: Verify the lifetime timeout value is appropriate for production.

The 60-second default lifetime timeout is a reasonable safety net, but this may be too short for routes with very slow data sources in production environments.

Consider making this configurable via environment variable or documenting the override via lifetimeMs option for users with long-running SSR scenarios.


294-393: Robust stream transformation with comprehensive error handling.

The async IIFE pattern correctly allows the stream to be returned immediately while processing happens asynchronously. The implementation properly:

  • Guards against processing after cleanup
  • Handles stream errors and reader release
  • Sets appropriate timeouts for serialization completion
  • Catches errors from both the transform loop and the outer promise
e2e/react-start/streaming-ssr/src/routes/deferred.tsx (2)

1-56: LGTM! Well-structured deferred data test route.

The route properly demonstrates the SSR streaming pattern with:

  • Immediate data (awaited server function)
  • Deferred promise (inline Promise)
  • Deferred server function call (not awaited)
  • Source tracking for SSR verification

The loader correctly mixes awaited and deferred data patterns.


78-89: The children prop usage is correct for TanStack Router's Await component.

The static analysis tool flags the children prop pattern, but this is the documented API for TanStack Router's Await component which accepts children as a render prop function. This is intentional and follows the library's conventions.

Also applies to: 92-105

e2e/react-start/streaming-ssr/tests/query-heavy.spec.ts (1)

1-131: LGTM! Comprehensive test coverage for query-heavy streaming SSR.

The test suite properly validates:

  • All 9 queries resolve with server data source
  • Correct values for sync and async queries
  • Hydration works correctly with many queries
  • Client-side navigation behavior
  • No hydration mismatch by verifying all data was streamed from server

The use of testWithHydration fixture and appropriate timeouts (5000ms) for async operations is well done.

packages/router-core/src/router.ts (1)

749-772: LGTM! Well-documented ServerSsr interface with synchronous buffering API.

The updated ServerSsr interface provides clear documentation for the new synchronous HTML/script injection model:

  • injectHtml and injectScript are now synchronous with buffering semantics
  • New lifecycle hooks (isSerializationFinished, onSerializationFinished) enable proper completion tracking
  • takeBufferedHtml complements the existing takeBufferedScripts for unified buffering

The JSDoc comments clearly explain the buffering behavior and when HTML is emitted vs. buffered.

e2e/react-start/streaming-ssr/src/routeTree.gen.ts (1)

1-261: Autogenerated file - no review needed.

Based on learnings, routeTree.gen.ts files in TanStack Router repositories are autogenerated and should not be manually modified or reviewed.

packages/router-core/src/ssr/ssr-server.ts (5)

58-136: LGTM! Well-designed ScriptBuffer with proper microtask batching.

The ScriptBuffer class improvements are solid:

  • Using slice() (line 68) correctly avoids mutating the shared INITIAL_SCRIPTS array
  • The _pendingMicrotask flag prevents redundant microtask scheduling
  • flush() ensures synchronous injection before signaling serialization completion
  • The takeAll() fix (line 118) correctly avoids mutation during iteration by joining directly

155-169: LGTM! Clean synchronous buffering for HTML injection.

The injectHtml and injectScript methods now follow the synchronous buffering pattern:

  • HTML is buffered immediately
  • Event emitted without promise payload
  • injectScript properly wraps content in script tag with nonce support

236-271: LGTM! Robust serialization completion handling.

The signalSerializationComplete function with try/catch/finally ensures:

  • Listeners are always notified
  • Event is always emitted
  • Listener arrays are always cleared (preventing memory leaks)
  • Errors in listeners don't prevent completion

The onDone handler correctly calls flush() before signaling completion, ensuring all scripts are injected synchronously before onSerializationFinished is emitted.


282-292: LGTM! Defensive error handling in setRenderFinished.

The try/catch/finally pattern ensures scriptBuffer.liftBarrier() is always called even if a listener throws, and listeners are cleared to prevent memory leaks.


310-326: LGTM! Complete cleanup implementation.

The takeBufferedHtml and cleanup methods properly manage the buffer lifecycle:

  • takeBufferedHtml joins and clears the buffer atomically
  • cleanup resets all listener arrays and buffers, preventing memory leaks

Comment on lines +15 to +17
"@tanstack/react-router": "workspace:^",
"@tanstack/react-router-ssr-query": "workspace:^",
"@tanstack/react-start": "workspace:^",
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

Use workspace:* protocol for internal dependencies per coding guidelines.

The workspace dependencies are currently using workspace:^, but the coding guidelines specify that internal dependencies should use workspace:*.

As per coding guidelines, the workspace protocol should be workspace:* for all internal TanStack packages.

🔎 Proposed fix
     "@tanstack/react-query": "^5.80.7",
-    "@tanstack/react-router": "workspace:^",
-    "@tanstack/react-router-ssr-query": "workspace:^",
-    "@tanstack/react-start": "workspace:^",
+    "@tanstack/react-router": "workspace:*",
+    "@tanstack/react-router-ssr-query": "workspace:*",
+    "@tanstack/react-start": "workspace:*",
     "react": "^19.0.0",
   "devDependencies": {
     "@playwright/test": "^1.50.1",
-    "@tanstack/router-e2e-utils": "workspace:^",
+    "@tanstack/router-e2e-utils": "workspace:*",
     "@types/node": "^22.10.2",

Also applies to: 24-24

🤖 Prompt for AI Agents
In e2e/react-start/streaming-ssr/package.json around lines 15 to 17 (and also
line 24), the workspace dependency protocol is set to "workspace:^" but must
follow the coding guideline of using "workspace:*" for internal TanStack
packages; update the listed dependencies "@tanstack/react-router",
"@tanstack/react-router-ssr-query", "@tanstack/react-start" (and the package at
line 24) to use the "workspace:*" protocol instead of "workspace:^".

Comment on lines +22 to +27
webServer: {
command: `VITE_SERVER_PORT=${PORT} pnpm build && PORT=${PORT} VITE_SERVER_PORT=${PORT} pnpm start`,
url: baseURL,
reuseExistingServer: !process.env.CI,
stdout: 'pipe',
},
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check usage of PORT and VITE_SERVER_PORT in the streaming-ssr project

# Search for PORT usage (excluding this config file)
echo "=== PORT usage ==="
rg -n '\bPORT\b' e2e/react-start/streaming-ssr --type ts --type tsx -g '!playwright.config.ts' 2>/dev/null || echo "No PORT matches"

echo ""
echo "=== VITE_SERVER_PORT usage ==="
rg -n 'VITE_SERVER_PORT' e2e/react-start/streaming-ssr --type ts --type tsx -g '!playwright.config.ts' 2>/dev/null || echo "No VITE_SERVER_PORT matches"

# Also check for package.json scripts
echo ""
echo "=== Check package.json for scripts ==="
find e2e/react-start/streaming-ssr -name 'package.json' -exec head -50 {} \; 2>/dev/null | grep -A 10 '"scripts"' || echo "No package.json found"

Repository: TanStack/router

Length of output: 617


Remove the unnecessary VITE_SERVER_PORT environment variable.

The VITE_SERVER_PORT variable is not used anywhere in the project and is not a standard Vite configuration variable. The PORT variable is necessary and used by srvx, which defaults to the PORT environment variable for the server's listening port. Simplify the command to:

command: `PORT=${PORT} pnpm build && pnpm start`,
🤖 Prompt for AI Agents
In e2e/react-start/streaming-ssr/playwright.config.ts around lines 22 to 27, the
webServer command sets an unused VITE_SERVER_PORT environment variable; remove
VITE_SERVER_PORT and simplify the command to only set PORT for both build and
start steps so the process uses the standard PORT env var (i.e., set
PORT=${PORT} for build and start and drop VITE_SERVER_PORT).

@schiller-manuel schiller-manuel merged commit c05572e into main Dec 21, 2025
4 of 5 checks passed
@schiller-manuel schiller-manuel deleted the fix-streaming branch December 21, 2025 23:13
@coderabbitai coderabbitai bot mentioned this pull request Dec 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants