Skip to content

Conversation

@birkskyum
Copy link
Member

@birkskyum birkskyum commented Oct 29, 2025

closes #5636

Summary by CodeRabbit

  • New Features

    • Added a /transition route (examples for multiple frameworks) demonstrating in-place data transitions.
  • Bug Fixes

    • Navigation now preserves previous view content during transitions and avoids unwanted loading flashes.
    • Route matching and component rendering made more resilient during navigation.
  • Tests

    • Added end-to-end tests ensuring transitions preserve prior data and update without "Loading...".
    • Updated unit test expectations and skipped one flaky navigation test.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 29, 2025

Walkthrough

Adds a new /transition file-based route and Playwright e2e tests across multiple examples; rewires generated route trees. Refactors router core and solid-router transition batching, match resolution, and useMatch error timing to tolerate in-flight navigations and preserve UI during transitions.

Changes

Cohort / File(s) Summary
New file routes & route trees (solid basic)
e2e/solid-router/basic-file-based/src/routes/transition/index.tsx, e2e/solid-router/basic-file-based/src/routeTree.gen.ts
Adds /transition route, search validation (n:number default 1), Suspense-backed component, and wires the route into generated route tree and public route maps.
E2E test (solid basic)
e2e/solid-router/basic-file-based/tests/transition.spec.ts
Adds Playwright test asserting previous UI remains visible (no "Loading...") during navigation and new values appear after transition.
New file routes & route trees (solid + query)
e2e/solid-router/basic-solid-query-file-based/src/routes/transition/index.tsx, e2e/solid-router/basic-solid-query-file-based/src/routeTree.gen.ts
Adds /transition route with solid-query loader & placeholderData, wires into generated route tree and public maps.
E2E test (solid + query)
e2e/solid-router/basic-solid-query-file-based/tests/transition.spec.ts
Adds Playwright test verifying placeholder/previous data preserved and no loading fallback during transition.
New file routes & route trees (react + query)
e2e/react-router/basic-react-query-file-based/src/routes/transition/index.tsx, e2e/react-router/basic-react-query-file-based/src/routeTree.gen.ts
Adds /transition React route with react-query loader/placeholderData and updates generated route tree and public typings.
E2E test (react + query)
e2e/react-router/basic-react-query-file-based/tests/transition.spec.ts
Adds Playwright test asserting react-query transitions preserve previous UI and avoid showing "Loading..." during navigation.
Router core batching / match computation
packages/router-core/src/router.ts
Runs match computation and state updates inside a startTransition wrapper; initializes match arrays explicitly; moves cache eviction into batch; invokes lifecycle hooks in-batch/within transition.
Solid-router Transitioner API
packages/solid-router/src/Transitioner.tsx
Changes router.startTransition from async wrapper to sync wrapper delegating to Solid.startTransition; ensures isTransitioning set/reset via try/finally.
Match rendering & Outlet safety
packages/solid-router/src/Match.tsx
Replaces invariant throws with guarded null/false returns, introduces resolvePendingComponent use, stabilizes Suspense usage, and tolerates temporarily missing matches during navigation.
useMatch error handling
packages/solid-router/src/useMatch.tsx
Decouples error throwing from selector by introducing matchState and reactive createEffect for throwing; selectors now return memoized match accessor.
Test updates & skips
packages/solid-router/tests/*, packages/react-router/tests/*
Adjusts numeric expectations in store-updates tests and marks one flaky useNavigate test as skipped.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant App as App / UI
  participant RouterCore as packages/router-core
  participant Transitioner as solid-router Transitioner
  participant MatchLayer as Match/Outlet components
  rect rgb(230,245,255)
    Note over App,RouterCore: Navigation initiated
    App->>RouterCore: request navigation / compute next matches
    RouterCore->>Transitioner: request startTransition(fn)
  end
  rect rgb(245,255,230)
    Note over Transitioner,RouterCore: run batched updates inside startTransition
    Transitioner->>RouterCore: execute fn inside Solid.startTransition (batched)
    RouterCore->>RouterCore: compute entering/exiting/staying matches
    RouterCore->>RouterCore: clearExpiredCache() (inside batch)
    RouterCore->>RouterCore: update match store (batched)
  end
  rect rgb(255,240,230)
    Note over RouterCore,MatchLayer: post-update render with guarded resolution
    RouterCore->>MatchLayer: invoke onLeave/onEnter/onStay handlers (within transition)
    MatchLayer-->>App: render using guarded matches / Suspense (no invariant throw)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Points to focus review on:

  • startTransition / batching boundary and hook ordering in packages/router-core/src/router.ts.
  • isTransitioning lifecycle and try/finally behavior in packages/solid-router/src/Transitioner.tsx.
  • Guarded rendering and Suspense interplay in packages/solid-router/src/Match.tsx (SSR/hydration edge cases).
  • useMatch error-throw timing (createEffect) in packages/solid-router/src/useMatch.tsx.
  • Flakiness/timing assumptions in new e2e Playwright tests across examples.

Possibly related PRs

Suggested reviewers

  • brenelz
  • schiller-manuel

Poem

🐇 A route sprang up with a gentle hop,

🌿 Old views held fast while new ones swap,

🐾 No loading cries disturbed the trail,

🌼 Smooth hops, calm landings — a quiet tale,

🎉 Cheers from the rabbit, nose-twitch and clap!

Pre-merge checks and finishing touches

✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR implements code changes across solid-router, router-core, and test suites that directly address issue #5636 by enabling manual Suspense boundaries to participate in navigation transitions through refactored state management and transition wrapping.
Out of Scope Changes check ✅ Passed While the PR includes changes to react-router and creates e2e tests for multiple frameworks, these are in-scope as they establish transition patterns and test suites that complement the core solid-router fixes and validate framework-agnostic transition behavior.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'fix(solid-router): implement navigation transitions' is concise, specific, and directly reflects the main change: fixing navigation transitions in solid-router by implementing the transition feature described in the linked issue #5636.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch solid-navigation-transition

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.

@nx-cloud
Copy link

nx-cloud bot commented Oct 29, 2025

View your CI Pipeline Execution ↗ for commit 3064313

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

☁️ Nx Cloud last updated this comment at 2025-11-04 02:44:52 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Oct 29, 2025

More templates

@tanstack/arktype-adapter

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

@tanstack/directive-functions-plugin

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

@tanstack/eslint-plugin-router

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

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/server-functions-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/zod-adapter

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

commit: 3064313

@birkskyum birkskyum marked this pull request as ready for review October 29, 2025 20:48
@birkskyum birkskyum changed the title solid navigation transitions fix(solid-router): make navigation transitions work Oct 29, 2025
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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ec3881 and 73b4984.

📒 Files selected for processing (5)
  • e2e/solid-router/basic-file-based/src/routeTree.gen.ts (11 hunks)
  • e2e/solid-router/basic-file-based/src/routes/transition/index.tsx (1 hunks)
  • e2e/solid-router/basic-file-based/tests/transition.spec.ts (1 hunks)
  • packages/router-core/src/router.ts (2 hunks)
  • packages/solid-router/src/Transitioner.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • e2e/solid-router/basic-file-based/tests/transition.spec.ts
  • packages/router-core/src/router.ts
  • packages/solid-router/src/Transitioner.tsx
  • e2e/solid-router/basic-file-based/src/routeTree.gen.ts
  • e2e/solid-router/basic-file-based/src/routes/transition/index.tsx
e2e/**

📄 CodeRabbit inference engine (AGENTS.md)

Store end-to-end tests under the e2e/ directory

Files:

  • e2e/solid-router/basic-file-based/tests/transition.spec.ts
  • e2e/solid-router/basic-file-based/src/routeTree.gen.ts
  • e2e/solid-router/basic-file-based/src/routes/transition/index.tsx
packages/router-core/**

📄 CodeRabbit inference engine (AGENTS.md)

Keep framework-agnostic core router logic in packages/router-core/

Files:

  • packages/router-core/src/router.ts
packages/{react-router,solid-router}/**

📄 CodeRabbit inference engine (AGENTS.md)

Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/

Files:

  • packages/solid-router/src/Transitioner.tsx
**/src/routes/**

📄 CodeRabbit inference engine (AGENTS.md)

Place file-based routes under src/routes/ directories

Files:

  • e2e/solid-router/basic-file-based/src/routes/transition/index.tsx
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{react-router,solid-router}/** : Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/
📚 Learning: 2025-10-08T08:11:47.088Z
Learnt from: nlynzaad
PR: TanStack/router#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:

  • e2e/solid-router/basic-file-based/tests/transition.spec.ts
  • e2e/solid-router/basic-file-based/src/routeTree.gen.ts
  • e2e/solid-router/basic-file-based/src/routes/transition/index.tsx
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/router-core/** : Keep framework-agnostic core router logic in packages/router-core/

Applied to files:

  • packages/router-core/src/router.ts
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{react-router,solid-router}/** : Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/

Applied to files:

  • packages/solid-router/src/Transitioner.tsx
  • e2e/solid-router/basic-file-based/src/routeTree.gen.ts
  • e2e/solid-router/basic-file-based/src/routes/transition/index.tsx
📚 Learning: 2025-10-01T18:31:35.420Z
Learnt from: schiller-manuel
PR: TanStack/router#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/solid-router/basic-file-based/src/routeTree.gen.ts
  • e2e/solid-router/basic-file-based/src/routes/transition/index.tsx
📚 Learning: 2025-09-28T21:41:45.233Z
Learnt from: nlynzaad
PR: TanStack/router#5284
File: e2e/react-start/basic/server.js:50-0
Timestamp: 2025-09-28T21:41:45.233Z
Learning: In Express v5, catch-all routes must use named wildcards. Use `/*splat` to match everything except root path, or `/{*splat}` (with braces) to match including root path. The old `*` syntax is not allowed and will cause "Missing parameter name" errors. This breaking change requires explicit naming of wildcard parameters.

Applied to files:

  • e2e/solid-router/basic-file-based/src/routeTree.gen.ts
📚 Learning: 2025-10-14T18:59:33.990Z
Learnt from: FatahChan
PR: TanStack/router#5475
File: e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx:8-0
Timestamp: 2025-10-14T18:59:33.990Z
Learning: In TanStack Router e2e test files, when a route parameter is validated at the route level (e.g., using zod in validateSearch or param validation), switch statements on that parameter do not require a default case, as the validation ensures only expected values will reach the switch.

Applied to files:

  • e2e/solid-router/basic-file-based/src/routes/transition/index.tsx
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to **/src/routes/** : Place file-based routes under src/routes/ directories

Applied to files:

  • e2e/solid-router/basic-file-based/src/routes/transition/index.tsx
🧬 Code graph analysis (2)
packages/router-core/src/router.ts (1)
packages/router-core/src/Matches.ts (1)
  • AnyRouteMatch (240-240)
packages/solid-router/src/Transitioner.tsx (1)
packages/solid-router/src/link.tsx (1)
  • isTransitioning (581-583)
⏰ 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). (1)
  • GitHub Check: Test
🔇 Additional comments (4)
packages/router-core/src/router.ts (2)

941-946: LGTM: Clean framework-agnostic extension point.

The wrapBatch hook provides a well-designed extension point for framework-specific transition handling. The default no-op implementation ensures backward compatibility while allowing frameworks like Solid to opt into custom batching behavior.

Based on coding guidelines


2105-2141: LGTM: Correct integration of wrapBatch for transition-aware batching.

The refactoring correctly hoists the match array declarations outside wrapBatch so they can be accessed by the lifecycle hooks (lines 2143-2154). The batch operation and cache cleanup are properly wrapped, enabling framework-specific transition handling while preserving the existing state update logic.

packages/solid-router/src/Transitioner.tsx (2)

64-71: LGTM: Proper async lifecycle management.

The try/finally block correctly ensures that isTransitioning is always reset, even if the navigation fails. The separation of concerns between tracking the transition state here and wrapping batch updates in wrapBatch is well-designed and clearly documented.

Based on coding guidelines


38-60: No actionable concerns found.

The jsdom detection in wrapBatch (line 48) is part of a multi-layered guard strategy that works correctly. The approach has three safety checks: isRealBrowser && !router.isServer && isTransitioning(). The test suite validates this behavior in both jsdom (standard tests) and server environments (server mode tests), confirming the detection works as intended. The fallback behavior—directly executing the callback—is safe in all scenarios where startTransition is skipped.

Comment on lines 17 to 39
const pollInterval = setInterval(async () => {
const text = await page.locator('body').textContent().catch(() => '')
if (text) bodyTexts.push(text)
}, 50)

// Click the increase button to trigger navigation with new search params
await page.getByTestId('increase-button').click()

// Wait a bit to capture text during the transition
await page.waitForTimeout(200)

clearInterval(pollInterval)

// Eventually, new values should appear
await expect(page.getByTestId('n-value')).toContainText('n: 2', {
timeout: 2000,
})
await expect(page.getByTestId('double-value')).toContainText('double: 4', {
timeout: 2000,
})
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 | 🟠 Major

Guard against leaking the polling interval on failures.

If any assertion between starting the interval and calling clearInterval throws (for example, the click hangs), the timer keeps running against a disposed page. That leaves an active handle in the worker and can make later tests flaky or hang. Please ensure the interval is always cleared via try/finally.

Apply this diff:

-  const pollInterval = setInterval(async () => {
-    const text = await page.locator('body').textContent().catch(() => '')
-    if (text) bodyTexts.push(text)
-  }, 50)
-
-  // Click the increase button to trigger navigation with new search params
-  await page.getByTestId('increase-button').click()
-
-  // Wait a bit to capture text during the transition
-  await page.waitForTimeout(200)
-
-  clearInterval(pollInterval)
+  const pollInterval = setInterval(async () => {
+    const text = await page.locator('body').textContent().catch(() => '')
+    if (text) bodyTexts.push(text)
+  }, 50)
+
+  try {
+    // Click the increase button to trigger navigation with new search params
+    await page.getByTestId('increase-button').click()
+
+    // Wait a bit to capture text during the transition
+    await page.waitForTimeout(200)
+  } finally {
+    clearInterval(pollInterval)
+  }
📝 Committable suggestion

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

Suggested change
const pollInterval = setInterval(async () => {
const text = await page.locator('body').textContent().catch(() => '')
if (text) bodyTexts.push(text)
}, 50)
// Click the increase button to trigger navigation with new search params
await page.getByTestId('increase-button').click()
// Wait a bit to capture text during the transition
await page.waitForTimeout(200)
clearInterval(pollInterval)
// Eventually, new values should appear
await expect(page.getByTestId('n-value')).toContainText('n: 2', {
timeout: 2000,
})
await expect(page.getByTestId('double-value')).toContainText('double: 4', {
timeout: 2000,
})
const pollInterval = setInterval(async () => {
const text = await page.locator('body').textContent().catch(() => '')
if (text) bodyTexts.push(text)
}, 50)
try {
// Click the increase button to trigger navigation with new search params
await page.getByTestId('increase-button').click()
// Wait a bit to capture text during the transition
await page.waitForTimeout(200)
} finally {
clearInterval(pollInterval)
}
// Eventually, new values should appear
await expect(page.getByTestId('n-value')).toContainText('n: 2', {
timeout: 2000,
})
await expect(page.getByTestId('double-value')).toContainText('double: 4', {
timeout: 2000,
})
🤖 Prompt for AI Agents
In e2e/solid-router/basic-file-based/tests/transition.spec.ts around lines 17 to
36, the polling interval started before the click is not guaranteed to be
cleared if an assertion or click throws; wrap the actions that depend on the
interval (the click, waitForTimeout, and the subsequent assertions) inside a try
block and call clearInterval(pollInterval) in a finally block so the timer is
always cleared even on failures; ensure the finally runs before making further
cleanup or test teardown.

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: 0

🧹 Nitpick comments (1)
packages/solid-router/src/Transitioner.tsx (1)

38-52: Consider caching the isRealBrowser check for performance.

The isRealBrowser computation runs on every wrapBatch invocation, which could be frequent during navigation. Since the browser environment doesn't change during the component lifecycle, consider computing this once during mount and storing it in a variable.

Apply this diff to optimize:

  const [isTransitioning, setIsTransitioning] = Solid.createSignal(false)
+
+ const isRealBrowser =
+   typeof window !== 'undefined' &&
+   typeof window.navigator !== 'undefined' &&
+   typeof window.navigator.userAgent === 'string' &&
+   !window.navigator.userAgent.includes('jsdom')

  router.wrapBatch = (fn: () => void) => {
-   const isRealBrowser =
-     typeof window !== 'undefined' &&
-     typeof window.navigator !== 'undefined' &&
-     typeof window.navigator.userAgent === 'string' &&
-     !window.navigator.userAgent.includes('jsdom')
-
    if (isRealBrowser && isTransitioning()) {
      Solid.startTransition(() => {
        fn()
      })
    } else {
      fn()
    }
  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 05672a2 and 16ab243.

📒 Files selected for processing (2)
  • packages/router-core/src/router.ts (2 hunks)
  • packages/solid-router/src/Transitioner.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • packages/solid-router/src/Transitioner.tsx
  • packages/router-core/src/router.ts
packages/{react-router,solid-router}/**

📄 CodeRabbit inference engine (AGENTS.md)

Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/

Files:

  • packages/solid-router/src/Transitioner.tsx
packages/router-core/**

📄 CodeRabbit inference engine (AGENTS.md)

Keep framework-agnostic core router logic in packages/router-core/

Files:

  • packages/router-core/src/router.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{react-router,solid-router}/** : Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/
Learnt from: nlynzaad
PR: TanStack/router#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.
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/router-core/** : Keep framework-agnostic core router logic in packages/router-core/
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{react-router,solid-router}/** : Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/

Applied to files:

  • packages/solid-router/src/Transitioner.tsx
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/router-core/** : Keep framework-agnostic core router logic in packages/router-core/

Applied to files:

  • packages/router-core/src/router.ts
🧬 Code graph analysis (2)
packages/solid-router/src/Transitioner.tsx (1)
packages/solid-router/src/link.tsx (1)
  • isTransitioning (581-583)
packages/router-core/src/router.ts (1)
packages/router-core/src/Matches.ts (1)
  • AnyRouteMatch (240-240)
⏰ 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: Preview
  • GitHub Check: Test
🔇 Additional comments (4)
packages/solid-router/src/Transitioner.tsx (2)

23-23: Minor formatting change.

The extra blank line improves readability.


54-61: Excellent error handling improvement.

The try/finally block ensures isTransitioning is always reset, even if the transition function throws. This prevents the router from getting stuck in a transitioning state.

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

949-954: Well-designed framework extension point.

The wrapBatch method provides a clean hook for framework-specific transition handling while keeping the core router framework-agnostic. The default no-op implementation and clear documentation make this easy for framework adapters to override.


2105-2141: Refactoring correctly enables framework-specific transition wrapping.

The refactored logic wraps the batch operation in wrapBatch, allowing framework adapters like Solid to inject transition handling around state updates. The match computation moved inside the wrapped context is necessary for Solid's reactivity tracking to work correctly.

The closure correctly captures exitingMatches, enteringMatches, and stayingMatches for use in lifecycle hooks after the batch completes.

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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 367f8bf and 6fc2d8b.

📒 Files selected for processing (2)
  • packages/solid-router/src/Transitioner.tsx (2 hunks)
  • packages/solid-start/src/useServerFn.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/solid-router/src/Transitioner.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • packages/solid-start/src/useServerFn.ts
packages/{*-start,start-*}/**

📄 CodeRabbit inference engine (AGENTS.md)

Name and place Start framework packages under packages/-start/ or packages/start-/

Files:

  • packages/solid-start/src/useServerFn.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{react-router,solid-router}/** : Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/
Learnt from: nlynzaad
PR: TanStack/router#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.
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/router-core/** : Keep framework-agnostic core router logic in packages/router-core/
📚 Learning: 2025-10-01T18:30:26.591Z
Learnt from: schiller-manuel
PR: TanStack/router#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-start/src/useServerFn.ts
⏰ 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). (1)
  • GitHub Check: Test
🔇 Additional comments (1)
packages/solid-start/src/useServerFn.ts (1)

21-26: Race condition concern is technically valid but requires architectural evaluation.

Concurrent server function calls do occur in practice (confirmed via Promise.all patterns in e2e tests), and the shared _isServerFnRedirect flag can indeed be cleared prematurely by the first redirect's finally() block while other navigations are in progress.

However, this pattern appears intentional for Solid's architecture—the comment states the flag exists to "skip Solid transitions" during server function redirects. React-start omits this flag entirely, suggesting different architectural needs between frameworks.

The suggested fix (passing flag as navigation options) requires verifying whether the router's navigation API supports this parameter. Alternatively, consider whether the race condition's actual impact (skipping the "skip transitions" behavior for concurrent redirects) warrants mitigation given the Solid-specific requirements.

err.options._fromLocation = router.state.location
return router.navigate(router.resolveRedirect(err).options)
// Mark this as a server function redirect to skip Solid transitions
;(router as any)._isServerFnRedirect = true
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 | 🟠 Major

Type safety violation: avoid any type assertion in strict TypeScript.

Using (router as any) bypasses TypeScript's type checking and defeats the purpose of strict mode. This creates a maintenance risk as the router's API evolves.

Consider one of these approaches:

Option 1: Add the flag to navigation options (preferred)

-        // Mark this as a server function redirect to skip Solid transitions
-        ;(router as any)._isServerFnRedirect = true
-        const result = router.navigate(router.resolveRedirect(err).options)
-        // Clear the flag after navigation starts
-        result.finally(() => {
-          ;(router as any)._isServerFnRedirect = false
-        })
-        return result
+        // Mark this as a server function redirect to skip Solid transitions
+        const result = router.navigate({
+          ...router.resolveRedirect(err).options,
+          _isServerFnRedirect: true,
+        })
+        return result

Option 2: Properly type the router extension

interface RouterWithServerFnFlag {
  _isServerFnRedirect?: boolean
}

// Then use:
(router as RouterWithServerFnFlag)._isServerFnRedirect = true

Option 1 is cleaner as it avoids shared mutable state and eliminates the need for the finally block.

🤖 Prompt for AI Agents
In packages/solid-start/src/useServerFn.ts around line 21 you assign a flag
using an any cast which breaks strict TypeScript; instead add a typed navigation
option (e.g., extend the existing navigate/redirect options type to include an
optional _isServerFnRedirect boolean) and pass that flag through the navigation
call so you can remove the (router as any) cast and the shared mutable
state/finally block; if you must keep the router mutation, declare a small
interface for the router extension with _isServerFnRedirect?: boolean and cast
to that interface rather than any.

@birkskyum birkskyum force-pushed the solid-navigation-transition branch from 6fc2d8b to 05672a2 Compare October 29, 2025 23:41
@birkskyum birkskyum requested review from brenelz and removed request for brenelz October 29, 2025 23:57
@birkskyum birkskyum force-pushed the solid-navigation-transition branch from 6b8192a to 29ffc5c Compare October 30, 2025 00:03
@birkskyum birkskyum added this to the catch up solid to react milestone Oct 30, 2025
@birkskyum birkskyum force-pushed the solid-navigation-transition branch 2 times, most recently from e1ef229 to 39dc575 Compare October 30, 2025 05:33
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: 0

🧹 Nitpick comments (1)
packages/solid-router/tests/router.test.tsx (1)

1681-1685: Consider aligning this test with the similar loader test or clarifying the intent.

The root route now includes a notFoundComponent, but the similar test above (lines 1640-1676, "loader throws a notFound()") creates the root route without this option. Additionally, the specific route at lines 1700-1702 already defines its own notFoundComponent with identical content.

If this change is intentional to test fallback behavior or component precedence, consider adding a comment explaining the difference. Otherwise, for consistency and clarity, align both tests to use the same pattern.

</review_comment_end>

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 39dc575 and 2f53719.

📒 Files selected for processing (1)
  • packages/solid-router/tests/router.test.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • packages/solid-router/tests/router.test.tsx
packages/{react-router,solid-router}/**

📄 CodeRabbit inference engine (AGENTS.md)

Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/

Files:

  • packages/solid-router/tests/router.test.tsx
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{react-router,solid-router}/** : Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/
Learnt from: nlynzaad
PR: TanStack/router#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.
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/router-core/** : Keep framework-agnostic core router logic in packages/router-core/
📚 Learning: 2025-10-08T08:11:47.088Z
Learnt from: nlynzaad
PR: TanStack/router#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/solid-router/tests/router.test.tsx
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{react-router,solid-router}/** : Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/

Applied to files:

  • packages/solid-router/tests/router.test.tsx
📚 Learning: 2025-10-14T18:59:33.990Z
Learnt from: FatahChan
PR: TanStack/router#5475
File: e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx:8-0
Timestamp: 2025-10-14T18:59:33.990Z
Learning: In TanStack Router e2e test files, when a route parameter is validated at the route level (e.g., using zod in validateSearch or param validation), switch statements on that parameter do not require a default case, as the validation ensures only expected values will reach the switch.

Applied to files:

  • packages/solid-router/tests/router.test.tsx
📚 Learning: 2025-10-01T18:30:26.591Z
Learnt from: schiller-manuel
PR: TanStack/router#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/tests/router.test.tsx
⏰ 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). (1)
  • GitHub Check: Test

@birkskyum birkskyum force-pushed the solid-navigation-transition branch 3 times, most recently from 5c9998d to 6b0d3e7 Compare November 2, 2025 13:21
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: 3

♻️ Duplicate comments (1)
e2e/solid-router/basic-file-based/tests/transition.spec.ts (1)

17-32: Always clear the polling interval.

If the click or wait throws, the interval keeps running against a torn-down page, leading to flaky tests. Wrap the interaction in try/finally and clear the interval in the finally block so it always tears down.

-  const pollInterval = setInterval(async () => {
-    const text = await page
-      .locator('body')
-      .textContent()
-      .catch(() => '')
-    if (text) bodyTexts.push(text)
-  }, 50)
-
-  // Click the increase button to trigger navigation with new search params
-  await page.getByTestId('increase-button').click()
-
-  // Wait a bit to capture text during the transition
-  await page.waitForTimeout(200)
-
-  clearInterval(pollInterval)
+  const pollInterval = setInterval(async () => {
+    const text = await page
+      .locator('body')
+      .textContent()
+      .catch(() => '')
+    if (text) bodyTexts.push(text)
+  }, 50)
+
+  try {
+    // Click the increase button to trigger navigation with new search params
+    await page.getByTestId('increase-button').click()
+
+    // Wait a bit to capture text during the transition
+    await page.waitForTimeout(200)
+  } finally {
+    clearInterval(pollInterval)
+  }
🧹 Nitpick comments (2)
packages/solid-router/tests/link.test.tsx (2)

785-793: Good synchronization for navigation side-effects.

The waitFor wrappers properly ensure that both the URL search parameters and DOM content have updated before assertions. This aligns with the PR's objective of handling navigation transitions correctly.

Consider consolidating the pathname assertion at line 788 into the first waitFor block for consistency:

     await waitFor(() => {
       expect(window.location.search).toBe('?page=2&filter=inactive')
+      expect(window.location.pathname).toBe('/posts')
     })
-    expect(window.location.pathname).toBe('/posts')

900-910: Navigation synchronization is correctly implemented.

The waitFor wrappers ensure navigation transitions complete before assertions, consistent with the fix for manual Suspense boundaries during navigation.

For consistency, consider consolidating the pathname assertion at line 903 into the first waitFor block:

     await waitFor(() => {
       expect(window.location.search).toBe('?page=2&filter=inactive')
+      expect(window.location.pathname).toBe('/Dashboard/posts')
     })
-    expect(window.location.pathname).toBe('/Dashboard/posts')
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c9998d and 6b0d3e7.

📒 Files selected for processing (12)
  • e2e/solid-router/basic-file-based/src/routeTree.gen.ts (11 hunks)
  • e2e/solid-router/basic-file-based/src/routes/transition/index.tsx (1 hunks)
  • e2e/solid-router/basic-file-based/tests/transition.spec.ts (1 hunks)
  • packages/react-router/tests/store-updates-during-navigation.test.tsx (1 hunks)
  • packages/router-core/src/router.ts (1 hunks)
  • packages/solid-router/src/Match.tsx (1 hunks)
  • packages/solid-router/src/Transitioner.tsx (3 hunks)
  • packages/solid-router/tests/link.test.tsx (4 hunks)
  • packages/solid-router/tests/optional-path-params.test.tsx (1 hunks)
  • packages/solid-router/tests/router.test.tsx (1 hunks)
  • packages/solid-router/tests/store-updates-during-navigation.test.tsx (8 hunks)
  • packages/solid-router/tests/useNavigate.test.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/router-core/src/router.ts
  • e2e/solid-router/basic-file-based/src/routes/transition/index.tsx
  • packages/solid-router/tests/optional-path-params.test.tsx
  • packages/solid-router/tests/useNavigate.test.tsx
  • packages/react-router/tests/store-updates-during-navigation.test.tsx
  • packages/solid-router/src/Match.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • e2e/solid-router/basic-file-based/tests/transition.spec.ts
  • packages/solid-router/tests/store-updates-during-navigation.test.tsx
  • packages/solid-router/src/Transitioner.tsx
  • e2e/solid-router/basic-file-based/src/routeTree.gen.ts
  • packages/solid-router/tests/link.test.tsx
  • packages/solid-router/tests/router.test.tsx
e2e/**

📄 CodeRabbit inference engine (AGENTS.md)

Store end-to-end tests under the e2e/ directory

Files:

  • e2e/solid-router/basic-file-based/tests/transition.spec.ts
  • e2e/solid-router/basic-file-based/src/routeTree.gen.ts
packages/{react-router,solid-router}/**

📄 CodeRabbit inference engine (AGENTS.md)

Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/

Files:

  • packages/solid-router/tests/store-updates-during-navigation.test.tsx
  • packages/solid-router/src/Transitioner.tsx
  • packages/solid-router/tests/link.test.tsx
  • packages/solid-router/tests/router.test.tsx
🧠 Learnings (9)
📓 Common learnings
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{react-router,solid-router}/** : Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/
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.
📚 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:

  • e2e/solid-router/basic-file-based/tests/transition.spec.ts
  • packages/solid-router/tests/store-updates-during-navigation.test.tsx
  • e2e/solid-router/basic-file-based/src/routeTree.gen.ts
  • packages/solid-router/tests/link.test.tsx
  • packages/solid-router/tests/router.test.tsx
📚 Learning: 2025-10-09T12:59:02.129Z
Learnt from: hokkyss
Repo: TanStack/router PR: 5418
File: e2e/react-start/custom-identifier-prefix/src/styles/app.css:19-21
Timestamp: 2025-10-09T12:59:02.129Z
Learning: In e2e test directories (paths containing `e2e/`), accessibility concerns like outline suppression patterns are less critical since the code is for testing purposes, not production use.

Applied to files:

  • e2e/solid-router/basic-file-based/tests/transition.spec.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/solid-router/basic-file-based/tests/transition.spec.ts
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{react-router,solid-router}/** : Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/

Applied to files:

  • packages/solid-router/tests/store-updates-during-navigation.test.tsx
  • packages/solid-router/src/Transitioner.tsx
  • e2e/solid-router/basic-file-based/src/routeTree.gen.ts
  • packages/solid-router/tests/link.test.tsx
  • packages/solid-router/tests/router.test.tsx
📚 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/solid-router/basic-file-based/src/routeTree.gen.ts
📚 Learning: 2025-09-28T21:41:45.233Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5284
File: e2e/react-start/basic/server.js:50-0
Timestamp: 2025-09-28T21:41:45.233Z
Learning: In Express v5, catch-all routes must use named wildcards. Use `/*splat` to match everything except root path, or `/{*splat}` (with braces) to match including root path. The old `*` syntax is not allowed and will cause "Missing parameter name" errors. This breaking change requires explicit naming of wildcard parameters.

Applied to files:

  • e2e/solid-router/basic-file-based/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/tests/router.test.tsx
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/router-core/** : Keep framework-agnostic core router logic in packages/router-core/

Applied to files:

  • packages/solid-router/tests/router.test.tsx
⏰ 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 (1)
packages/solid-router/tests/link.test.tsx (1)

396-399: LGTM! Proper synchronization for navigation transition.

The addition of waitFor ensures the navigation has completed and the link has acquired the 'active' class before proceeding with subsequent assertions. This properly handles the asynchronous nature of navigation transitions.

Comment on lines +38 to 47
router.startTransition = (fn: () => void | Promise<void>) => {
setIsTransitioning(true)
await fn()
setIsTransitioning(false)
Solid.startTransition(async () => {
try {
await fn()
} finally {
setIsTransitioning(false)
}
})
}
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 | 🔴 Critical

Don’t hand Solid.startTransition an async callback.

Solid’s startTransition expects a synchronous callback. Giving it an async function means the awaited part runs after Solid has already ended the transition, so updates escape the transition and errors become unhandled rejections. Run fn synchronously (capture its promise separately) and only await it outside the transition to keep semantics intact.

🤖 Prompt for AI Agents
In packages/solid-router/src/Transitioner.tsx around lines 38 to 47, the code
passes an async callback to Solid.startTransition which breaks transition
semantics; instead call Solid.startTransition with a synchronous function that
invokes fn() but does not await it, capture the returned promise in a local
variable, then await that promise outside the transition in a finally block that
resets setIsTransitioning(false) so the awaited work runs after Solid ends the
transition and any errors can be handled properly.

⚠️ Potential issue | 🔴 Critical

Maintain startTransition’s async contract.

router.startTransition used to resolve once the wrapped work finished. After this refactor it always returns void, so any callers still doing await router.startTransition(...) now resume immediately, which can break sequencing and let isTransitioning drop before the work finishes. Please restore the async return (e.g. by returning the Promise from Solid.startTransition).

🤖 Prompt for AI Agents
In packages/solid-router/src/Transitioner.tsx around lines 38 to 47,
router.startTransition currently returns void causing callers that await it to
resume immediately; restore the async contract by returning a Promise that
resolves when the wrapped work completes. Change the implementation to return
the Promise from Solid.startTransition (or wrap the async work in a new Promise)
so callers can await completion, ensure setIsTransitioning(true) is set before
starting and setIsTransitioning(false) in finally after the awaited work, and
propagate any errors to the returned Promise.

@birkskyum birkskyum force-pushed the solid-navigation-transition branch 5 times, most recently from 0dad6bb to 5c1dd3d Compare November 3, 2025 19:24
@birkskyum birkskyum force-pushed the solid-navigation-transition branch from 847434d to a37c72d Compare November 4, 2025 02:00
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: 2

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/Match.tsx (2)

28-105: Early null guard still crashes when the match briefly disappears

Returning null the moment matchState() is falsy only affects the very first invocation of this component. During a transition the router can temporarily drop the match (the scenario this PR is trying to support), so matchState() later flips to null. Because the component body doesn’t re-run, every closure we created below (route(), resolvePendingComponent(), routeNotFoundComponent(), …) still dereferences matchState()!.routeId and we blow up with TypeError: Cannot read properties of null (reading 'routeId') as soon as the reproduction from #5636 navigates while a loader is pending.

We need to make the guard reactive—either wrap the whole render tree in a <Show when={matchState()}> and define the dependent helpers inside, or rework each helper to tolerate undefined (return early before touching .routeId). For example:

-  if (!matchState()) return null
-
-  const route: () => AnyRoute = () =>
-    router.routesById[matchState()!.routeId]
+  const state = Solid.createMemo(matchState)
+
+  return (
+    <Solid.Show when={state()} fallback={null}>
+      {(current) => {
+        const route = () => router.routesById[current().routeId]!
+        // …rest of the logic that currently lives below
+      }}
+    </Solid.Show>
+  )

Without moving the rest of the logic under a reactive guard we still crash under the manual Suspense transition, so this is a blocker.


186-352: MatchInner has the same unsafe guard

The same early-return pattern shows up here: if (!matchState()) return null only protects the initial mount. Once the router briefly removes the match, every helper below (route(), match(), componentKey(), the pending branch that reads route().options…, etc.) still closes over matchState()!.… and throws. You can hit this by running the linked Suspense repro and navigating during a pending transition—the stack trace now originates from MatchInner.

Please restructure this component the same way as Match: gate the rest of the logic behind a reactive guard (e.g. a <Show when={matchState()}> block) or make each helper tolerate undefined before touching the match. Until we do that, navigation transitions still crash, so we can’t ship this.

🧹 Nitpick comments (1)
e2e/solid-router/basic-solid-query-file-based/src/routes/transition/index.tsx (1)

32-34: Remove redundant createMemo wrapper.

useQuery already returns a reactive signal in Solid, so wrapping it in createMemo is unnecessary and adds overhead.

-  const doubleQuery = createMemo(() =>
-    useQuery(() => doubleQueryOptions(search().n)),
-  )
+  const doubleQuery = useQuery(() => doubleQueryOptions(search().n))

Update line 50 to:

-          <div data-testid="double-value">double: {doubleQuery().data}</div>
+          <div data-testid="double-value">double: {doubleQuery.data}</div>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a37c72d and e0364bb.

📒 Files selected for processing (5)
  • e2e/solid-router/basic-file-based/tests/transition.spec.ts (1 hunks)
  • e2e/solid-router/basic-solid-query-file-based/src/routeTree.gen.ts (9 hunks)
  • e2e/solid-router/basic-solid-query-file-based/src/routes/transition/index.tsx (1 hunks)
  • e2e/solid-router/basic-solid-query-file-based/tests/transition.spec.ts (1 hunks)
  • packages/solid-router/src/Match.tsx (11 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • e2e/solid-router/basic-file-based/tests/transition.spec.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • e2e/solid-router/basic-solid-query-file-based/src/routes/transition/index.tsx
  • e2e/solid-router/basic-solid-query-file-based/tests/transition.spec.ts
  • e2e/solid-router/basic-solid-query-file-based/src/routeTree.gen.ts
  • packages/solid-router/src/Match.tsx
**/src/routes/**

📄 CodeRabbit inference engine (AGENTS.md)

Place file-based routes under src/routes/ directories

Files:

  • e2e/solid-router/basic-solid-query-file-based/src/routes/transition/index.tsx
e2e/**

📄 CodeRabbit inference engine (AGENTS.md)

Store end-to-end tests under the e2e/ directory

Files:

  • e2e/solid-router/basic-solid-query-file-based/src/routes/transition/index.tsx
  • e2e/solid-router/basic-solid-query-file-based/tests/transition.spec.ts
  • e2e/solid-router/basic-solid-query-file-based/src/routeTree.gen.ts
packages/{react-router,solid-router}/**

📄 CodeRabbit inference engine (AGENTS.md)

Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/

Files:

  • packages/solid-router/src/Match.tsx
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{react-router,solid-router}/** : Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/
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.
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/router-core/** : Keep framework-agnostic core router logic in packages/router-core/
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{react-router,solid-router}/** : Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/

Applied to files:

  • e2e/solid-router/basic-solid-query-file-based/src/routes/transition/index.tsx
  • e2e/solid-router/basic-solid-query-file-based/src/routeTree.gen.ts
  • packages/solid-router/src/Match.tsx
📚 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:

  • e2e/solid-router/basic-solid-query-file-based/src/routes/transition/index.tsx
  • e2e/solid-router/basic-solid-query-file-based/tests/transition.spec.ts
  • e2e/solid-router/basic-solid-query-file-based/src/routeTree.gen.ts
  • packages/solid-router/src/Match.tsx
📚 Learning: 2025-10-14T18:59:33.990Z
Learnt from: FatahChan
Repo: TanStack/router PR: 5475
File: e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx:8-0
Timestamp: 2025-10-14T18:59:33.990Z
Learning: In TanStack Router e2e test files, when a route parameter is validated at the route level (e.g., using zod in validateSearch or param validation), switch statements on that parameter do not require a default case, as the validation ensures only expected values will reach the switch.

Applied to files:

  • e2e/solid-router/basic-solid-query-file-based/src/routes/transition/index.tsx
  • packages/solid-router/src/Match.tsx
📚 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/solid-router/basic-solid-query-file-based/src/routeTree.gen.ts
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to **/src/routes/** : Place file-based routes under src/routes/ directories

Applied to files:

  • e2e/solid-router/basic-solid-query-file-based/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/Match.tsx
🧬 Code graph analysis (2)
e2e/solid-router/basic-solid-query-file-based/src/routeTree.gen.ts (1)
e2e/solid-router/basic-file-based/src/routeTree.gen.ts (1)
  • FileRoutesByTo (667-747)
packages/solid-router/src/Match.tsx (3)
packages/router-core/src/route.ts (1)
  • AnyRoute (778-797)
packages/router-core/src/index.ts (2)
  • AnyRoute (162-162)
  • rootRouteId (110-110)
packages/react-router/src/Match.tsx (1)
  • Match (26-146)
⏰ 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 (1)
e2e/solid-router/basic-solid-query-file-based/src/routes/transition/index.tsx (1)

6-55: Excellent demonstration of navigation transition handling.

The implementation correctly uses placeholderData: (oldData) => oldData (line 17) to preserve previous query data during navigation, which prevents the Suspense fallback from triggering. Combined with ensureQueryData in the loader, this achieves smooth transitions where the old content remains visible while new data loads.

Comment on lines +11 to +36
const bodySnapshots: Array<string> = []

const interval = setInterval(async () => {
const text = await page
.locator('body')
.textContent()
.catch(() => '')
if (text) bodySnapshots.push(text)
}, 50)

await page.getByTestId('increase-button').click()

await page.waitForTimeout(200)

clearInterval(interval)

await expect(page.getByTestId('n-value')).toContainText('n: 2', {
timeout: 2_000,
})
await expect(page.getByTestId('double-value')).toContainText('double: 4', {
timeout: 2_000,
})

const sawLoading = bodySnapshots.some((text) => text.includes('Loading...'))

expect(sawLoading).toBeFalsy()
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 | 🔴 Critical

Critical flaw in test timing logic.

The interval stops capturing snapshots at 200ms (line 25), but the expectations wait up to 2 seconds for updated values (lines 27-32). If the "Loading..." fallback appears between 200ms and when the values actually update, the test won't detect it—creating a false positive.

Move clearInterval(interval) to after the final expectations to ensure the entire transition period is monitored:

-  await page.waitForTimeout(200)
-
-  clearInterval(interval)
-
   await expect(page.getByTestId('n-value')).toContainText('n: 2', {
     timeout: 2_000,
   })
   await expect(page.getByTestId('double-value')).toContainText('double: 4', {
     timeout: 2_000,
   })
+
+  clearInterval(interval)
 
   const sawLoading = bodySnapshots.some((text) => text.includes('Loading...'))
📝 Committable suggestion

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

Suggested change
const bodySnapshots: Array<string> = []
const interval = setInterval(async () => {
const text = await page
.locator('body')
.textContent()
.catch(() => '')
if (text) bodySnapshots.push(text)
}, 50)
await page.getByTestId('increase-button').click()
await page.waitForTimeout(200)
clearInterval(interval)
await expect(page.getByTestId('n-value')).toContainText('n: 2', {
timeout: 2_000,
})
await expect(page.getByTestId('double-value')).toContainText('double: 4', {
timeout: 2_000,
})
const sawLoading = bodySnapshots.some((text) => text.includes('Loading...'))
expect(sawLoading).toBeFalsy()
const bodySnapshots: Array<string> = []
const interval = setInterval(async () => {
const text = await page
.locator('body')
.textContent()
.catch(() => '')
if (text) bodySnapshots.push(text)
}, 50)
await page.getByTestId('increase-button').click()
await expect(page.getByTestId('n-value')).toContainText('n: 2', {
timeout: 2_000,
})
await expect(page.getByTestId('double-value')).toContainText('double: 4', {
timeout: 2_000,
})
clearInterval(interval)
const sawLoading = bodySnapshots.some((text) => text.includes('Loading...'))
expect(sawLoading).toBeFalsy()
🤖 Prompt for AI Agents
In e2e/solid-router/basic-solid-query-file-based/tests/transition.spec.ts around
lines 11 to 36, the snapshot-capturing interval is cleared before the test waits
(and retries) for the final values, so any transient "Loading..." state that
occurs after 200ms but before the expectations resolve will be missed; move
clearInterval(interval) to after the final expect checks (i.e., after the two
toContainText awaits and just before asserting sawLoading) so the interval
continues recording for the entire timeout window.

Comment on lines +13 to +25
const interval = setInterval(async () => {
const text = await page
.locator('body')
.textContent()
.catch(() => '')
if (text) bodySnapshots.push(text)
}, 50)

await page.getByTestId('increase-button').click()

await page.waitForTimeout(200)

clearInterval(interval)
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 | 🟠 Major

Add cleanup for interval to prevent resource leak.

If the test fails or times out before line 25, setInterval will continue running. Wrap the interval logic in a try-finally block or use Playwright's test.afterEach for cleanup.

+  let interval: NodeJS.Timeout | undefined
+
-  const interval = setInterval(async () => {
+  try {
+    interval = setInterval(async () => {
+      const text = await page
+        .locator('body')
+        .textContent()
+        .catch(() => '')
+      if (text) bodySnapshots.push(text)
+    }, 50)
+
+    await page.getByTestId('increase-button').click()
+
+    await expect(page.getByTestId('n-value')).toContainText('n: 2', {
+      timeout: 2_000,
+    })
+    await expect(page.getByTestId('double-value')).toContainText('double: 4', {
+      timeout: 2_000,
+    })
+  } finally {
+    if (interval) clearInterval(interval)
+  }
-    const text = await page
-      .locator('body')
-      .textContent()
-      .catch(() => '')
-    if (text) bodySnapshots.push(text)
-  }, 50)
-
-  await page.getByTestId('increase-button').click()
-
-  clearInterval(interval)
-
-  await expect(page.getByTestId('n-value')).toContainText('n: 2', {
-    timeout: 2_000,
-  })
-  await expect(page.getByTestId('double-value')).toContainText('double: 4', {
-    timeout: 2_000,
-  })

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In e2e/solid-router/basic-solid-query-file-based/tests/transition.spec.ts around
lines 13 to 25, the setInterval created to poll page body content is not
guaranteed to be cleared if the test fails or times out; wrap the interval logic
so it is always cleaned up—for example, create the interval, run the async test
actions inside a try block, and call clearInterval(interval) in a finally block,
or register clearInterval with Playwright's test.afterEach cleanup (or
test.addCleanup) so the interval is always cleared even on failure/timeouts.

@birkskyum birkskyum removed this from the catch up solid to react milestone Nov 4, 2025
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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e0364bb and ca69d40.

📒 Files selected for processing (3)
  • e2e/react-router/basic-react-query-file-based/src/routeTree.gen.ts (9 hunks)
  • e2e/react-router/basic-react-query-file-based/src/routes/transition/index.tsx (1 hunks)
  • e2e/react-router/basic-react-query-file-based/tests/transition.spec.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • e2e/react-router/basic-react-query-file-based/tests/transition.spec.ts
  • e2e/react-router/basic-react-query-file-based/src/routes/transition/index.tsx
  • e2e/react-router/basic-react-query-file-based/src/routeTree.gen.ts
e2e/**

📄 CodeRabbit inference engine (AGENTS.md)

Store end-to-end tests under the e2e/ directory

Files:

  • e2e/react-router/basic-react-query-file-based/tests/transition.spec.ts
  • e2e/react-router/basic-react-query-file-based/src/routes/transition/index.tsx
  • e2e/react-router/basic-react-query-file-based/src/routeTree.gen.ts
**/src/routes/**

📄 CodeRabbit inference engine (AGENTS.md)

Place file-based routes under src/routes/ directories

Files:

  • e2e/react-router/basic-react-query-file-based/src/routes/transition/index.tsx
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{react-router,solid-router}/** : Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/
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.
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/router-core/** : Keep framework-agnostic core router logic in packages/router-core/
Learnt from: FatahChan
Repo: TanStack/router PR: 5475
File: e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx:8-0
Timestamp: 2025-10-14T18:59:33.990Z
Learning: In TanStack Router e2e test files, when a route parameter is validated at the route level (e.g., using zod in validateSearch or param validation), switch statements on that parameter do not require a default case, as the validation ensures only expected values will reach the switch.
📚 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:

  • e2e/react-router/basic-react-query-file-based/tests/transition.spec.ts
  • e2e/react-router/basic-react-query-file-based/src/routeTree.gen.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-router/basic-react-query-file-based/src/routeTree.gen.ts
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to **/src/routes/** : Place file-based routes under src/routes/ directories

Applied to files:

  • e2e/react-router/basic-react-query-file-based/src/routeTree.gen.ts
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{react-router,solid-router}/** : Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/

Applied to files:

  • e2e/react-router/basic-react-query-file-based/src/routeTree.gen.ts
🧬 Code graph analysis (1)
e2e/react-router/basic-react-query-file-based/src/routeTree.gen.ts (1)
e2e/solid-router/basic-solid-query-file-based/src/routeTree.gen.ts (1)
  • FileRoutesByTo (75-82)
🪛 Biome (2.1.2)
e2e/react-router/basic-react-query-file-based/src/routes/transition/index.tsx

[error] 33-33: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

@birkskyum birkskyum added this to the catch up solid to react milestone Nov 4, 2025
@birkskyum birkskyum changed the title fix(solid-router): make navigation transitions work fix(solid-router): implement navigation transitions Nov 4, 2025
@birkskyum birkskyum merged commit 76c4f61 into main Nov 4, 2025
6 checks passed
@birkskyum birkskyum deleted the solid-navigation-transition branch November 4, 2025 02:51
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.

solid-start: manual suspense boundaries don't transition on navigation

3 participants