Conversation
📝 WalkthroughWalkthroughAdds an end-to-end React Start context-bridge e2e app and docs, a createStartContextBridge utility with server dehydration/client hydration wiring, router and route changes to consume bridged context, SSR-query plugin migration to plugin API across integrations, Playwright tests, and router-core plugin typing and wiring changes. Changes
Sequence Diagram(s)sequenceDiagram
participant Test as Playwright
participant DevServer as Vite Dev Server
participant Server as Start Instance
participant Browser as Client
participant Router as TanStack Router Plugin System
Test->>DevServer: start webServer (build + serve)
DevServer->>Server: forward HTTP requests
Server->>Server: mwA -> mwB -> mwC (augment request context)
Server->>Server: router.dehydrate includes bridged data under bridgeKey
Server->>Browser: serve HTML with dehydrated payload
Browser->>Router: createRouter(...) with plugins (ssr-query, start-context-bridge)
Router->>Router: plugin.hydrate merges bridged data into router.context
Browser->>Router: render route -> component reads bridged context
Test->>Browser: navigate to /next, Router handles client navigation and context persists
Estimated code review effort🎯 4 (Complex) | ⏱️ ~40 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
|
| Command | Status | Duration | Result |
|---|---|---|---|
nx affected --targets=test:eslint,test:unit,tes... |
❌ Failed | 13m 50s | View ↗ |
nx run-many --target=build --exclude=examples/*... |
✅ Succeeded | 1m 48s | View ↗ |
☁️ Nx Cloud last updated this comment at 2026-02-06 18:53:26 UTC
615893a to
3eec39b
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@e2e/react-start/context-bridge/package.json`:
- Around line 14-30: Update the package.json dependency specifiers for internal
TanStack packages to use the workspace protocol wildcard: replace "workspace:^"
with "workspace:*" for the packages named "@tanstack/react-router",
"@tanstack/react-router-devtools", "@tanstack/react-start", and
"@tanstack/router-e2e-utils" so they use workspace:* instead of workspace:^.
In `@e2e/react-start/context-bridge/src/routes/__root.tsx`:
- Around line 23-58: RootDocument renders the lazily-loaded RouterDevtools
(RouterDevtools) directly which will throw without a Suspense boundary; update
RootDocument to wrap the RouterDevtools invocation in a React.Suspense boundary
(with a lightweight fallback such as null or a small loader) so the lazy import
resolves correctly, and ensure React.Suspense is imported/available in the file;
keep the existing production-guarded RouterDevtools definition unchanged.
In `@e2e/react-start/context-bridge/vite.config.ts`:
- Around line 7-10: The Vite config currently hardcodes server.port to 3000
inside the defineConfig call; update the server.port assignment in
vite.config.ts to read process.env.VITE_SERVER_PORT and parse it as an integer
(fallback to 3000 when missing) so Vite respects the port Playwright provides;
modify the server.port expression used in defineConfig accordingly (use
parseInt(process.env.VITE_SERVER_PORT) with a default).
🧹 Nitpick comments (2)
e2e/react-start/context-bridge/src/utils/sortJson.ts (1)
1-17: Tighten typing to JSON values to avoid unsafe casts.The current
as T/as unknown as Tcasts sidestep strict typing. Consider a JSON-specific type to keep this utility safe and self-documenting.♻️ Suggested refactor
+type JsonPrimitive = string | number | boolean | null +type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue } + -export function sortJson<T>(value: T): T { +export function sortJson(value: JsonValue): JsonValue { if (Array.isArray(value)) { - return value.map(sortJson) as T + return value.map(sortJson) } if (!value || typeof value !== 'object') { return value } - const obj = value as Record<string, unknown> - const out: Record<string, unknown> = {} + const obj = value as Record<string, JsonValue> + const out: Record<string, JsonValue> = {} for (const key of Object.keys(obj).sort()) { out[key] = sortJson(obj[key]) } - return out as T + return out }As per coding guidelines: Use TypeScript strict mode with extensive type safety for all code.
packages/start-client-core/src/createStartContextBridge.ts (1)
70-80: Avoidanyon the hydrate payload to preserve strict typing.
dehydrated: anyand related casts bypass the strict-mode intent. Preferunknown+ narrowing (or a shared Dehydrated type) to keep safety intact.♻️ Suggested refactor
- router.options.hydrate = async (dehydrated: any) => { - await ogHydrate?.(dehydrated) - const selected = dehydrated?.[key] as - | Record<string, unknown> - | undefined - if (!selected) return + router.options.hydrate = async (dehydrated: unknown) => { + await ogHydrate?.(dehydrated as unknown) + if (!dehydrated || typeof dehydrated !== 'object') return + const selected = (dehydrated as Record<string, unknown>)[key] + if (!selected || typeof selected !== 'object') return router.update({ - context: safeObjectMerge(router.options.context, selected), + context: safeObjectMerge( + router.options.context, + selected as Record<string, unknown>, + ), }) }As per coding guidelines: Use TypeScript strict mode with extensive type safety for all code.
| "dependencies": { | ||
| "@tanstack/react-router": "workspace:^", | ||
| "@tanstack/react-router-devtools": "workspace:^", | ||
| "@tanstack/react-start": "workspace:^", | ||
| "express": "^5.1.0", | ||
| "http-proxy-middleware": "^3.0.5", | ||
| "react": "^19.0.0", | ||
| "react-dom": "^19.0.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@playwright/test": "^1.50.1", | ||
| "@tailwindcss/vite": "^4.1.18", | ||
| "@tanstack/router-e2e-utils": "workspace:^", | ||
| "@types/node": "^22.10.2", | ||
| "@types/react": "^19.0.8", | ||
| "@types/react-dom": "^19.0.3", | ||
| "@vitejs/plugin-react": "^4.3.4", |
There was a problem hiding this comment.
Use workspace:* for internal TanStack dependencies.
🔧 Suggested update
"dependencies": {
- "@tanstack/react-router": "workspace:^",
- "@tanstack/react-router-devtools": "workspace:^",
- "@tanstack/react-start": "workspace:^",
+ "@tanstack/react-router": "workspace:*",
+ "@tanstack/react-router-devtools": "workspace:*",
+ "@tanstack/react-start": "workspace:*",
"express": "^5.1.0",
"http-proxy-middleware": "^3.0.5",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@playwright/test": "^1.50.1",
"@tailwindcss/vite": "^4.1.18",
- "@tanstack/router-e2e-utils": "workspace:^",
+ "@tanstack/router-e2e-utils": "workspace:*",As per coding guidelines: Use workspace protocol workspace:* for internal dependencies in package.json files.
📝 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.
| "dependencies": { | |
| "@tanstack/react-router": "workspace:^", | |
| "@tanstack/react-router-devtools": "workspace:^", | |
| "@tanstack/react-start": "workspace:^", | |
| "express": "^5.1.0", | |
| "http-proxy-middleware": "^3.0.5", | |
| "react": "^19.0.0", | |
| "react-dom": "^19.0.0" | |
| }, | |
| "devDependencies": { | |
| "@playwright/test": "^1.50.1", | |
| "@tailwindcss/vite": "^4.1.18", | |
| "@tanstack/router-e2e-utils": "workspace:^", | |
| "@types/node": "^22.10.2", | |
| "@types/react": "^19.0.8", | |
| "@types/react-dom": "^19.0.3", | |
| "@vitejs/plugin-react": "^4.3.4", | |
| "dependencies": { | |
| "@tanstack/react-router": "workspace:*", | |
| "@tanstack/react-router-devtools": "workspace:*", | |
| "@tanstack/react-start": "workspace:*", | |
| "express": "^5.1.0", | |
| "http-proxy-middleware": "^3.0.5", | |
| "react": "^19.0.0", | |
| "react-dom": "^19.0.0" | |
| }, | |
| "devDependencies": { | |
| "@playwright/test": "^1.50.1", | |
| "@tailwindcss/vite": "^4.1.18", | |
| "@tanstack/router-e2e-utils": "workspace:*", | |
| "@types/node": "^22.10.2", | |
| "@types/react": "^19.0.8", | |
| "@types/react-dom": "^19.0.3", | |
| "@vitejs/plugin-react": "^4.3.4", |
🤖 Prompt for AI Agents
In `@e2e/react-start/context-bridge/package.json` around lines 14 - 30, Update the
package.json dependency specifiers for internal TanStack packages to use the
workspace protocol wildcard: replace "workspace:^" with "workspace:*" for the
packages named "@tanstack/react-router", "@tanstack/react-router-devtools",
"@tanstack/react-start", and "@tanstack/router-e2e-utils" so they use
workspace:* instead of workspace:^.
| const RouterDevtools = | ||
| process.env.NODE_ENV === 'production' | ||
| ? () => null | ||
| : React.lazy(() => | ||
| import('@tanstack/react-router-devtools').then((res) => ({ | ||
| default: res.TanStackRouterDevtools, | ||
| })), | ||
| ) | ||
|
|
||
| function RootDocument({ children }: { children: React.ReactNode }) { | ||
| return ( | ||
| <html> | ||
| <head> | ||
| <HeadContent /> | ||
| </head> | ||
| <body> | ||
| <div className="p-2 flex gap-3 text-lg"> | ||
| <Link | ||
| to="/" | ||
| activeOptions={{ exact: true }} | ||
| activeProps={{ className: 'font-bold' }} | ||
| > | ||
| Home | ||
| </Link> | ||
| <Link | ||
| to="/next" | ||
| activeOptions={{ exact: true }} | ||
| activeProps={{ className: 'font-bold' }} | ||
| > | ||
| Next | ||
| </Link> | ||
| </div> | ||
| <hr /> | ||
| {children} | ||
| <RouterDevtools position="bottom-right" /> | ||
| <Scripts /> |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
React.lazy Suspense boundary requirement React 18 19 runtime error
💡 Result:
In React 18 and React 19, a React.lazy() component must be rendered under an ancestor <Suspense> boundary because rendering a lazy component while its code is still loading “suspends” (it throws a Promise). React then walks up the rendered tree looking for the nearest <Suspense> to show its fallback; if it can’t find one, you’ll get a runtime failure/blank screen. [1]
Key “boundary requirement” gotchas that commonly cause runtime errors:
- The
<Suspense>must be above the lazy component in the rendered tree. A<Suspense>inside the component that suspends won’t help, because React can’t reach it when that component suspends during its own render. [2] - If you see
A component suspended while responding to synchronous input…in React 18+, it usually means a user-driven synchronous update (navigation, typing, click) caused something to suspend (often a lazy route or Suspense-based data). Typical fixes are:- move/add a higher Suspense boundary (e.g., wrap your router / route outlet), and/or
- wrap the update that triggers the suspend in
startTransition. [3]
Minimal correct pattern
import { Suspense, lazy } from "react";
const Page = lazy(() => import("./Page"));
export function App() {
return (
<Suspense fallback={<div>Loading…</div>}>
<Page />
</Suspense>
);
}Sources: React lazy docs (v19.2) [1], explanation of boundary placement (React walks up to nearest <Suspense>) [2], React 18 runtime error (“suspended while responding to synchronous input”) discussion and fixes [3].
[1] (react.dev)
[2] (stackoverflow.com)
[3] (stackoverflow.com)
Citations:
- 1: https://react.dev/reference/react/lazy?utm_source=openai
- 2: https://stackoverflow.com/questions/77485440/react-query-suspense-issue-suspended-while-responding-to-synchronous-input?utm_source=openai
- 3: https://stackoverflow.com/questions/72167518/a-component-suspended-while-responding-to-synchronous-input?utm_source=openai
Wrap the lazy devtools in a Suspense boundary.
Rendering a React.lazy component without Suspense will throw at runtime. The <Suspense> boundary must be placed above the lazy component in the rendered tree for React to properly handle code-splitting and suspense.
✅ Suggested fix
- <RouterDevtools position="bottom-right" />
+ <React.Suspense fallback={null}>
+ <RouterDevtools position="bottom-right" />
+ </React.Suspense>📝 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.
| const RouterDevtools = | |
| process.env.NODE_ENV === 'production' | |
| ? () => null | |
| : React.lazy(() => | |
| import('@tanstack/react-router-devtools').then((res) => ({ | |
| default: res.TanStackRouterDevtools, | |
| })), | |
| ) | |
| function RootDocument({ children }: { children: React.ReactNode }) { | |
| return ( | |
| <html> | |
| <head> | |
| <HeadContent /> | |
| </head> | |
| <body> | |
| <div className="p-2 flex gap-3 text-lg"> | |
| <Link | |
| to="/" | |
| activeOptions={{ exact: true }} | |
| activeProps={{ className: 'font-bold' }} | |
| > | |
| Home | |
| </Link> | |
| <Link | |
| to="/next" | |
| activeOptions={{ exact: true }} | |
| activeProps={{ className: 'font-bold' }} | |
| > | |
| Next | |
| </Link> | |
| </div> | |
| <hr /> | |
| {children} | |
| <RouterDevtools position="bottom-right" /> | |
| <Scripts /> | |
| const RouterDevtools = | |
| process.env.NODE_ENV === 'production' | |
| ? () => null | |
| : React.lazy(() => | |
| import('@tanstack/react-router-devtools').then((res) => ({ | |
| default: res.TanStackRouterDevtools, | |
| })), | |
| ) | |
| function RootDocument({ children }: { children: React.ReactNode }) { | |
| return ( | |
| <html> | |
| <head> | |
| <HeadContent /> | |
| </head> | |
| <body> | |
| <div className="p-2 flex gap-3 text-lg"> | |
| <Link | |
| to="/" | |
| activeOptions={{ exact: true }} | |
| activeProps={{ className: 'font-bold' }} | |
| > | |
| Home | |
| </Link> | |
| <Link | |
| to="/next" | |
| activeOptions={{ exact: true }} | |
| activeProps={{ className: 'font-bold' }} | |
| > | |
| Next | |
| </Link> | |
| </div> | |
| <hr /> | |
| {children} | |
| <React.Suspense fallback={null}> | |
| <RouterDevtools position="bottom-right" /> | |
| </React.Suspense> | |
| <Scripts /> |
🤖 Prompt for AI Agents
In `@e2e/react-start/context-bridge/src/routes/__root.tsx` around lines 23 - 58,
RootDocument renders the lazily-loaded RouterDevtools (RouterDevtools) directly
which will throw without a Suspense boundary; update RootDocument to wrap the
RouterDevtools invocation in a React.Suspense boundary (with a lightweight
fallback such as null or a small loader) so the lazy import resolves correctly,
and ensure React.Suspense is imported/available in the file; keep the existing
production-guarded RouterDevtools definition unchanged.
| export default defineConfig({ | ||
| server: { | ||
| port: 3000, | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify port alignment between Vite and Playwright config
rg -n "getTestServerPort|baseURL|port" e2e/react-start/context-bridge/playwright.config.ts e2e/react-start/context-bridge/vite.config.tsRepository: TanStack/router
Length of output: 1541
🏁 Script executed:
cat e2e/react-start/context-bridge/vite.config.tsRepository: TanStack/router
Length of output: 504
🏁 Script executed:
cat e2e/react-start/context-bridge/playwright.config.tsRepository: TanStack/router
Length of output: 788
Vite config must respect the VITE_SERVER_PORT environment variable that Playwright passes.
The vite.config.ts hardcodes port: 3000, but playwright.config.ts is configured to pass a dynamically-derived port via the VITE_SERVER_PORT environment variable (line 16 in playwright.config.ts: VITE_SERVER_PORT=${PORT}). The Vite config ignores this, causing test failures when the Playwright webServer tries to reach the dev server on the derived port while Vite listens on 3000.
Update vite.config.ts to read the environment variable:
server: {
port: process.env.VITE_SERVER_PORT ? parseInt(process.env.VITE_SERVER_PORT) : 3000,
},
🤖 Prompt for AI Agents
In `@e2e/react-start/context-bridge/vite.config.ts` around lines 7 - 10, The Vite
config currently hardcodes server.port to 3000 inside the defineConfig call;
update the server.port assignment in vite.config.ts to read
process.env.VITE_SERVER_PORT and parse it as an integer (fallback to 3000 when
missing) so Vite respects the port Playwright provides; modify the server.port
expression used in defineConfig accordingly (use
parseInt(process.env.VITE_SERVER_PORT) with a default).
3eec39b to
2cd5db8
Compare
2cd5db8 to
43b1e54
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/solid-router/src/router.ts (1)
67-73:⚠️ Potential issue | 🟡 MinorJSDoc links reference React instead of Solid framework.
The
defaultOnCatchproperty documentation links point to React framework docs instead of Solid:
- Line 70:
docs/framework/react/api/router/...- Line 71:
docs/framework/react/guide/...These should reference the Solid framework documentation for consistency with the rest of this file.
📝 Proposed fix
/** * The default `onCatch` handler for errors caught by the Router ErrorBoundary * - * `@link` [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultoncatch-property) - * `@link` [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#handling-errors-with-routeoptionsoncatch) + * `@link` [API Docs](https://tanstack.com/router/latest/docs/framework/solid/api/router/RouterOptionsType#defaultoncatch-property) + * `@link` [Guide](https://tanstack.com/router/latest/docs/framework/solid/guide/data-loading#handling-errors-with-routeoptionsoncatch) */ defaultOnCatch?: (error: Error) => voiddocs/router/integrations/query.md (1)
150-155:⚠️ Potential issue | 🟡 MinorBug:
contextis not defined in the loader scope.The loader function destructures
paramsbutcontextis not destructured. The code should be:export const Route = createFileRoute('/user/$id')({ - loader: ({ params }) => { + loader: ({ context, params }) => { // do not await this nor return the promise, just kick off the query to stream it to the client context.queryClient.fetchQuery(userQuery(params.id)) }, })
🤖 Fix all issues with AI agents
In `@e2e/react-start/multi-plugin/package.json`:
- Around line 17-20: Update the internal `@tanstack` package dependency versions
to use the workspace protocol with a wildcard: replace instances of
"workspace:^" with "workspace:*" for the packages "@tanstack/react-router",
"@tanstack/react-router-devtools", "@tanstack/react-router-ssr-query", and
"@tanstack/react-start"; ensure all four dependency entries use "workspace:*" so
they match the project's workspace dependency guideline.
In `@e2e/react-start/multi-plugin/src/routes/__root.tsx`:
- Around line 3-9: The import list includes an unused symbol Outlet which ESLint
flags; remove Outlet from the named imports at the top (the import statement
that currently imports HeadContent, Link, Outlet, Scripts,
createRootRouteWithContext) so only used symbols remain (e.g., HeadContent,
Link, Scripts, createRootRouteWithContext), ensuring no other references to
Outlet exist in the file before committing.
- Around line 25-60: Wrap the lazy RouterDevtools usage in a React Suspense
boundary inside RootDocument: when rendering <RouterDevtools
position="bottom-right" /> replace it with a Suspense wrapper (e.g.,
<React.Suspense fallback={null}>...</React.Suspense>) so the lazy-loaded
RouterDevtools can suspend safely; keep ReactQueryDevtools as-is and ensure you
reference the existing RouterDevtools symbol and the RootDocument component.
🧹 Nitpick comments (3)
packages/router-core/src/index.ts (1)
459-464: Consider using@internalinstead of@privatefor these exports.The
@privateJSDoc annotation is inconsistent with these being public exports from the package index. Since the AI summary indicates these types are consumed by other packages (react-router, solid-router, vue-router), consider using@internalto indicate they're meant for internal TanStack package use only, or remove the annotation if external consumption is acceptable.📝 Suggested annotation change
-/** `@private` */ +/** `@internal` */ export type { RouterPlugin, RouterPluginTypes, PluginProvidedContext, } from './plugin'docs/start/framework/react/guide/context-bridge.md (1)
115-119: Consecutive blockquotes should not have blank lines between them.The markdownlint tool flags a blank line between blockquotes (MD028). If these are related warnings, consider merging them into a single blockquote or separating them with regular text.
📝 Suggested fix
> [!IMPORTANT] > The return value of `select` must be serializable. Do not include functions, class instances, or other non-serializable values. The type system will warn you if you try to return non-serializable data. - +> > [!WARNING] > Be careful not to bridge sensitive values (API keys, secrets, tokens) since they will be serialized and sent to the client in the HTML payload.Alternatively, add explanatory text between them if they represent distinct concerns.
packages/router-core/src/router.ts (1)
4-4: ESLint import order flag (optional fix).Static analysis indicates the type import from
./pluginshould occur after the import from./rewriteper the project's import ordering rules.🔧 Suggested reorder
Move the
./pluginimport after the./rewriteimport at line 44:import { composeRewrites, executeRewriteInput, executeRewriteOutput, rewriteBasepath, } from './rewrite' +import type { PluginProvidedContext, RouterPlugin } from './plugin'And remove it from line 4.
| "@tanstack/react-router": "workspace:^", | ||
| "@tanstack/react-router-devtools": "workspace:^", | ||
| "@tanstack/react-router-ssr-query": "workspace:^", | ||
| "@tanstack/react-start": "workspace:^", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find all workspace protocol usages in e2e package.json files
find e2e -name "package.json" -type f -exec grep -l "workspace:" {} \; | while read file; do
echo "=== $file ==="
grep "workspace:" "$file" | head -20
doneRepository: TanStack/router
Length of output: 28399
Change workspace protocol to workspace:* for all internal dependencies.
The coding guideline requires using workspace:* for internal dependencies, but this file uses workspace:^ on lines 17-20. Update all internal @tanstack package dependencies to use workspace:* instead of workspace:^ for consistency with the documented requirements.
🤖 Prompt for AI Agents
In `@e2e/react-start/multi-plugin/package.json` around lines 17 - 20, Update the
internal `@tanstack` package dependency versions to use the workspace protocol
with a wildcard: replace instances of "workspace:^" with "workspace:*" for the
packages "@tanstack/react-router", "@tanstack/react-router-devtools",
"@tanstack/react-router-ssr-query", and "@tanstack/react-start"; ensure all four
dependency entries use "workspace:*" so they match the project's workspace
dependency guideline.
| import { | ||
| HeadContent, | ||
| Link, | ||
| Outlet, | ||
| Scripts, | ||
| createRootRouteWithContext, | ||
| } from '@tanstack/react-router' |
There was a problem hiding this comment.
Remove unused Outlet import.
ESLint flags it as unused.
🧹 Proposed fix
import {
HeadContent,
Link,
- Outlet,
Scripts,
createRootRouteWithContext,
} from '@tanstack/react-router'📝 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.
| import { | |
| HeadContent, | |
| Link, | |
| Outlet, | |
| Scripts, | |
| createRootRouteWithContext, | |
| } from '@tanstack/react-router' | |
| import { | |
| HeadContent, | |
| Link, | |
| Scripts, | |
| createRootRouteWithContext, | |
| } from '@tanstack/react-router' |
🧰 Tools
🪛 ESLint
[error] 6-6: 'Outlet' is defined but never used.
(unused-imports/no-unused-imports)
🤖 Prompt for AI Agents
In `@e2e/react-start/multi-plugin/src/routes/__root.tsx` around lines 3 - 9, The
import list includes an unused symbol Outlet which ESLint flags; remove Outlet
from the named imports at the top (the import statement that currently imports
HeadContent, Link, Outlet, Scripts, createRootRouteWithContext) so only used
symbols remain (e.g., HeadContent, Link, Scripts, createRootRouteWithContext),
ensuring no other references to Outlet exist in the file before committing.
| const RouterDevtools = | ||
| process.env.NODE_ENV === 'production' | ||
| ? () => null | ||
| : React.lazy(() => | ||
| import('@tanstack/react-router-devtools').then((res) => ({ | ||
| default: res.TanStackRouterDevtools, | ||
| })), | ||
| ) | ||
|
|
||
| function RootDocument({ children }: { children: React.ReactNode }) { | ||
| return ( | ||
| <html> | ||
| <head> | ||
| <HeadContent /> | ||
| </head> | ||
| <body> | ||
| <div className="p-2 flex gap-3 text-lg"> | ||
| <Link | ||
| to="/" | ||
| activeOptions={{ exact: true }} | ||
| activeProps={{ className: 'font-bold' }} | ||
| > | ||
| Home | ||
| </Link> | ||
| <Link | ||
| to="/query-test" | ||
| activeOptions={{ exact: true }} | ||
| activeProps={{ className: 'font-bold' }} | ||
| > | ||
| Query Test | ||
| </Link> | ||
| </div> | ||
| <hr /> | ||
| {children} | ||
| <RouterDevtools position="bottom-right" /> | ||
| <ReactQueryDevtools buttonPosition="bottom-left" /> |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
React.lazy() Suspense requirement documentation
💡 Result:
React docs explicitly state that a component created with React.lazy() must be rendered within a <Suspense> boundary so React has a fallback UI to show while the code chunk is loading. You can wrap the lazy component itself or any parent above it in the tree. [1] [2]
Official documentation pages:
lazyreference (shows “Lazy-loading components with Suspense” and the requirement to wrap in<Suspense>): [1]<Suspense>reference (explains how Suspense showsfallbackwhen children “suspend,” including lazy-loaded code): [2]- Legacy “Code-Splitting” guide (states “The lazy component should then be rendered inside a
Suspensecomponent”): [3]
Sources:
[1] https://react.dev/reference/react/lazy
[2] https://react.dev/reference/react/Suspense
[3] https://legacy.reactjs.org/docs/code-splitting.html
🏁 Script executed:
cat -n e2e/react-start/multi-plugin/src/routes/__root.tsx | head -70Repository: TanStack/router
Length of output: 2182
Wrap the lazy RouterDevtools in Suspense.
React.lazy components must be rendered inside a Suspense boundary per React documentation. In development, RouterDevtools is a lazy component that will suspend while loading; without Suspense, this causes runtime warnings/errors.
✅ Suggested fix
- <RouterDevtools position="bottom-right" />
+ <React.Suspense fallback={null}>
+ <RouterDevtools position="bottom-right" />
+ </React.Suspense>🤖 Prompt for AI Agents
In `@e2e/react-start/multi-plugin/src/routes/__root.tsx` around lines 25 - 60,
Wrap the lazy RouterDevtools usage in a React Suspense boundary inside
RootDocument: when rendering <RouterDevtools position="bottom-right" /> replace
it with a Suspense wrapper (e.g., <React.Suspense
fallback={null}>...</React.Suspense>) so the lazy-loaded RouterDevtools can
suspend safely; keep ReactQueryDevtools as-is and ensure you reference the
existing RouterDevtools symbol and the RootDocument component.

Summary by CodeRabbit
New Features
Tests
Documentation
Chores