Skip to content

test: client-nav benchmarks use a more representative app#6812

Open
Sheraff wants to merge 2 commits intomainfrom
test-benchmarks-client-nav-more-representative-app
Open

test: client-nav benchmarks use a more representative app#6812
Sheraff wants to merge 2 commits intomainfrom
test-benchmarks-client-nav-more-representative-app

Conversation

@Sheraff
Copy link
Contributor

@Sheraff Sheraff commented Mar 3, 2026

make the client-nav benchmark more representative of a "full Router app" by

  • using useMatch too
  • beforeLoad, loader, loaderDeps, validateSearch route options
  • sometimes navigating the search, sometimes the params
  • add some weight to re-renders, not only re-select

Summary by CodeRabbit

  • Tests
    • Updated client-side navigation benchmarks for React, Solid, and Vue.
    • Added a new match-based component to measure render behavior.
    • Adjusted navigation payload timing to update route params less frequently for finer re-render testing.
    • Reduced perf-loop iterations for faster benchmarking and added route lifecycle hooks (validation, pre-load, loader dependencies) to exercise load/validation flows.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 3, 2026

📝 Walkthrough

Walkthrough

The PR adds a Match component and imports useMatch across React/Solid/Vue benchmarks, reduces selector loop iterations from 100 to 50, changes navigation param computation to update params less frequently, and extends route declarations with validateSearch, beforeLoad, loaderDeps, and loader hooks.

Changes

Cohort / File(s) Summary
Route config & Match component
benchmarks/client-nav/react/app.tsx, benchmarks/client-nav/solid/app.tsx, benchmarks/client-nav/vue/app.tsx
Added useMatch import and a new Match component; Params and Search now return computed numbers directly; perf loop reduced to 50 iterations; route declarations extended with validateSearch, beforeLoad, loaderDeps, and loader.
Navigation parameter adjustment (benchmarks)
benchmarks/client-nav/react/speed.bench.tsx, benchmarks/client-nav/solid/speed.bench.tsx, benchmarks/client-nav/vue/speed.bench.tsx
Changed id param calculation to Math.floor((nextId + 1) / 2) (params update every 2 navigations) and added explanatory comments to test granular re-render behavior.

Sequence Diagram(s)

sequenceDiagram
  participant Bench as Bench Client
  participant Router as Router
  participant Route as Route (validateSearch / beforeLoad / loaderDeps / loader)
  participant Component as Component (Match/Params/Search)

  Bench->>Router: navigate(to, params, search)
  Router->>Route: validateSearch(search)
  Route-->>Router: validatedSearch
  Router->>Route: beforeLoad()
  Route-->>Router: Promise.resolve()
  Router->>Route: loaderDeps({search})
  Route-->>Router: deps
  Router->>Route: loader()
  Route-->>Router: Promise.resolve()
  Router->>Component: render with params/search/match
  Component-->>Bench: updated UI
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

package: react-router, package: solid-router, package: vue-router

Suggested reviewers

  • schiller-manuel
  • nlynzaad

Poem

🐰 I hopped through routes both near and far,
A Match I made beneath the router star,
Loops shortened, params skip a beat,
Benchmarks hum with nimble feet,
Rabbity cheers — the code's complete! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'test: client-nav benchmarks use a more representative app' clearly and directly summarizes the main change—updating benchmark tests to be more realistic by enhancing the app structure.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch test-benchmarks-client-nav-more-representative-app

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

@nx-cloud
Copy link

nx-cloud bot commented Mar 3, 2026

🤖 Nx Cloud AI Fix Eligible

An automatically generated fix could have helped fix failing tasks for this run, but Self-healing CI is disabled for this workspace. Visit workspace settings to enable it and get automatic fixes in future runs.

To disable these notifications, a workspace admin can disable them in workspace settings.


View your CI Pipeline Execution ↗ for commit 016e842

Command Status Duration Result
nx run @benchmarks/client-nav:test:perf:solid ❌ Failed 1m 16s View ↗
nx run @benchmarks/ssr:test:perf:vue ✅ Succeeded 2m 9s View ↗
nx run @benchmarks/ssr:test:perf:solid ✅ Succeeded 1m 55s View ↗
nx run @benchmarks/ssr:test:perf:react ✅ Succeeded 2m 4s View ↗
nx run @benchmarks/client-nav:test:perf:vue ✅ Succeeded 1m 28s View ↗
nx run @benchmarks/client-nav:test:perf:react ✅ Succeeded 1m 30s View ↗

☁️ Nx Cloud last updated this comment at 2026-03-03 18:14:28 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 3, 2026

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/@tanstack/arktype-adapter@6812

@tanstack/eslint-plugin-router

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

@tanstack/history

npm i https://pkg.pr.new/@tanstack/history@6812

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

npm i https://pkg.pr.new/@tanstack/react-start@6812

@tanstack/react-start-client

npm i https://pkg.pr.new/@tanstack/react-start-client@6812

@tanstack/react-start-server

npm i https://pkg.pr.new/@tanstack/react-start-server@6812

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

npm i https://pkg.pr.new/@tanstack/solid-start@6812

@tanstack/solid-start-client

npm i https://pkg.pr.new/@tanstack/solid-start-client@6812

@tanstack/solid-start-server

npm i https://pkg.pr.new/@tanstack/solid-start-server@6812

@tanstack/start-client-core

npm i https://pkg.pr.new/@tanstack/start-client-core@6812

@tanstack/start-fn-stubs

npm i https://pkg.pr.new/@tanstack/start-fn-stubs@6812

@tanstack/start-plugin-core

npm i https://pkg.pr.new/@tanstack/start-plugin-core@6812

@tanstack/start-server-core

npm i https://pkg.pr.new/@tanstack/start-server-core@6812

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

npm i https://pkg.pr.new/@tanstack/start-storage-context@6812

@tanstack/valibot-adapter

npm i https://pkg.pr.new/@tanstack/valibot-adapter@6812

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/@tanstack/virtual-file-routes@6812

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

npm i https://pkg.pr.new/@tanstack/vue-start@6812

@tanstack/vue-start-client

npm i https://pkg.pr.new/@tanstack/vue-start-client@6812

@tanstack/vue-start-server

npm i https://pkg.pr.new/@tanstack/vue-start-server@6812

@tanstack/zod-adapter

npm i https://pkg.pr.new/@tanstack/zod-adapter@6812

commit: c8f20af

@github-actions
Copy link

github-actions bot commented Mar 3, 2026

Bundle Size Benchmarks

  • Commit: 2217f7c3f19f
  • Measured at: 2026-03-03T18:00:48.036Z
  • Baseline source: history:2217f7c3f19f
  • Dashboard: bundle-size history
Scenario Current (gzip) Delta vs baseline Raw Brotli Trend
react-router.minimal 86.58 KiB 0 B (0.00%) 272.45 KiB 75.22 KiB ▅▅▅▅▅▅▅▅▅▅▅
react-router.full 89.61 KiB 0 B (0.00%) 282.78 KiB 77.90 KiB ▅▅▅▅▅▅▅▅▅▅▅
solid-router.minimal 35.88 KiB 0 B (0.00%) 107.56 KiB 32.26 KiB ▅▅▅▅▅▅▅▅▅▅▅
solid-router.full 40.21 KiB 0 B (0.00%) 120.61 KiB 36.13 KiB ▅▅▅▅▅▅▅▅▅▅▅
vue-router.minimal 51.75 KiB 0 B (0.00%) 147.54 KiB 46.50 KiB ▅▅▅▅▅▅▅▅▅▅▅
vue-router.full 56.55 KiB 0 B (0.00%) 163.12 KiB 50.86 KiB ▅▅▅▅▅▅▅▅▅▅▅
react-start.minimal 99.11 KiB 0 B (0.00%) 311.58 KiB 85.68 KiB ▅▅▅▅▅▅▅▅▅▅▅
react-start.full 102.50 KiB 0 B (0.00%) 321.39 KiB 88.63 KiB ▁▁▁████████
solid-start.minimal 48.19 KiB 0 B (0.00%) 145.13 KiB 42.67 KiB ▅▅▅▅▅▅▅▅▅▅▅
solid-start.full 53.68 KiB 0 B (0.00%) 161.08 KiB 47.37 KiB ▁▁▁████████

Trend sparkline is historical gzip bytes ending with this PR measurement; lower is better.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 11d0bd4a61

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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.

🧹 Nitpick comments (4)
benchmarks/client-nav/vue/app.tsx (1)

100-104: Consider adding type annotations for search parameter.

Per TypeScript strict mode guidelines, the search parameter in validateSearch and loaderDeps is implicitly typed. While this works for a benchmark file, explicit typing would improve safety.

♻️ Optional: Add explicit type annotation
 const route = createRoute({
   getParentRoute: () => root,
   path: '/$id',
-  validateSearch: (search) => ({ id: search.id }),
+  validateSearch: (search: Record<string, unknown>) => ({ id: search.id as string }),
   component: () => <div />,
   beforeLoad: () => Promise.resolve(),
-  loaderDeps: ({ search }) => ({ id: search.id }),
+  loaderDeps: ({ search }: { search: { id: string } }) => ({ id: search.id }),
   loader: () => Promise.resolve(),
 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@benchmarks/client-nav/vue/app.tsx` around lines 100 - 104, The validateSearch
and loaderDeps functions use an implicitly typed search parameter; add an
explicit type for search (e.g., define or import a Search type/interface) and
annotate the parameter in validateSearch (validateSearch: (search: Search) =>
...) and in loaderDeps (loaderDeps: ({ search }: { search: Search }) => ... or
equivalent destructured typing) so TypeScript strict mode is satisfied; ensure
any downstream returns ({ id: search.id }) remain unchanged and update any
related types used by loader and beforeLoad if needed to keep signatures
consistent with the new Search type.
benchmarks/client-nav/solid/speed.bench.tsx (1)

41-42: Use explicit string conversion in navigation payload to avoid implicit coercion.

For stricter typing and clearer intent, serialize both values at the callsite instead of relying on downstream coercion.

Proposed diff
       return navigate({
         to: '/$id',
-        params: { id: Math.floor((nextId + 1) / 2) },
-        search: { id: Math.floor(nextId / 2) },
+        params: { id: String(Math.floor((nextId + 1) / 2)) },
+        search: { id: String(Math.floor(nextId / 2)) },
         replace: true,
       })

As per coding guidelines: **/*.{ts,tsx}: Use TypeScript strict mode with extensive type safety.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@benchmarks/client-nav/solid/speed.bench.tsx` around lines 41 - 42, The
numeric values assigned to params and search are implicitly coerced from nextId;
update the callsite to explicitly serialize both values to strings (e.g., use
String(nextIdExpression) or .toString()) for params.id and search.id to satisfy
strict TypeScript typing and avoid implicit coercion; locate the object with
keys params and search that reference nextId and replace the numeric expressions
(Math.floor((nextId + 1) / 2) and Math.floor(nextId / 2)) with their stringified
equivalents.
benchmarks/client-nav/solid/app.tsx (1)

78-84: Normalize search.id in validateSearch to keep downstream types stable.

validateSearch currently forwards search.id as-is, so loaderDeps inherits a weakly-typed value. Normalizing once here keeps search typing deterministic across the route lifecycle.

Proposed diff
 const route = createRoute({
   getParentRoute: () => root,
   path: '/$id',
-  validateSearch: (search) => ({ id: search.id }),
+  validateSearch: (search) => ({
+    id:
+      typeof search.id === 'string'
+        ? search.id
+        : String(search.id ?? '0'),
+  }),
   component: () => {
     return <div />
   },
   beforeLoad: () => Promise.resolve(),
   loaderDeps: ({ search }) => ({ id: search.id }),
   loader: () => Promise.resolve(),
 })

As per coding guidelines: **/*.{ts,tsx}: Use TypeScript strict mode with extensive type safety.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@benchmarks/client-nav/solid/app.tsx` around lines 78 - 84, validateSearch is
forwarding search.id raw which makes downstream types in loaderDeps unstable;
update validateSearch to normalize id (e.g., return { id: String(search.id ??
'') } or another explicit normalized type your route expects) so the route
lifecycle has a deterministic type for id; change the implementation of
validateSearch (and confirm loaderDeps: ({ search }) => ({ id: search.id }) now
receives the normalized/typed id) to keep types strict and consistent across
validateSearch, loaderDeps, and loader.
benchmarks/client-nav/react/app.tsx (1)

85-89: Consider type-safe validation in benchmarks only if consistency is preferred across frameworks.

While stricter input validation for search.id would align with TypeScript's strict mode expectations, this is a performance benchmark where minimal overhead is intentional. The three framework implementations (React, Solid, Vue) all use identical pass-through validation, and the actual performance-sensitive code in the component already normalizes the value where it matters (line 37: Number(search.id ?? 0)). The loaders and component handlers are no-ops, so the validation doesn't affect benchmark accuracy. Adopt the suggested normalization only if cross-framework consistency or downstream type contracts require stricter schemas.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@benchmarks/client-nav/react/app.tsx` around lines 85 - 89, The current
pass-through validation in validateSearch and loaderDeps yields ({ id: search.id
}) which is intentional for minimal overhead in benchmarks; if you decide to
enforce type-safe normalization for cross-framework consistency, update
validateSearch and loaderDeps to normalize id (e.g., convert to a number with
Number(search.id ?? 0)) so downstream code (including component and loader)
receives a predictable numeric id, and adjust any related loader or loaderDeps
expectations accordingly; otherwise leave the existing pass-through
implementations (validateSearch, loaderDeps, component, loader) as-is to avoid
adding benchmark overhead.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@benchmarks/client-nav/react/app.tsx`:
- Around line 85-89: The current pass-through validation in validateSearch and
loaderDeps yields ({ id: search.id }) which is intentional for minimal overhead
in benchmarks; if you decide to enforce type-safe normalization for
cross-framework consistency, update validateSearch and loaderDeps to normalize
id (e.g., convert to a number with Number(search.id ?? 0)) so downstream code
(including component and loader) receives a predictable numeric id, and adjust
any related loader or loaderDeps expectations accordingly; otherwise leave the
existing pass-through implementations (validateSearch, loaderDeps, component,
loader) as-is to avoid adding benchmark overhead.

In `@benchmarks/client-nav/solid/app.tsx`:
- Around line 78-84: validateSearch is forwarding search.id raw which makes
downstream types in loaderDeps unstable; update validateSearch to normalize id
(e.g., return { id: String(search.id ?? '') } or another explicit normalized
type your route expects) so the route lifecycle has a deterministic type for id;
change the implementation of validateSearch (and confirm loaderDeps: ({ search
}) => ({ id: search.id }) now receives the normalized/typed id) to keep types
strict and consistent across validateSearch, loaderDeps, and loader.

In `@benchmarks/client-nav/solid/speed.bench.tsx`:
- Around line 41-42: The numeric values assigned to params and search are
implicitly coerced from nextId; update the callsite to explicitly serialize both
values to strings (e.g., use String(nextIdExpression) or .toString()) for
params.id and search.id to satisfy strict TypeScript typing and avoid implicit
coercion; locate the object with keys params and search that reference nextId
and replace the numeric expressions (Math.floor((nextId + 1) / 2) and
Math.floor(nextId / 2)) with their stringified equivalents.

In `@benchmarks/client-nav/vue/app.tsx`:
- Around line 100-104: The validateSearch and loaderDeps functions use an
implicitly typed search parameter; add an explicit type for search (e.g., define
or import a Search type/interface) and annotate the parameter in validateSearch
(validateSearch: (search: Search) => ...) and in loaderDeps (loaderDeps: ({
search }: { search: Search }) => ... or equivalent destructured typing) so
TypeScript strict mode is satisfied; ensure any downstream returns ({ id:
search.id }) remain unchanged and update any related types used by loader and
beforeLoad if needed to keep signatures consistent with the new Search type.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2217f7c and 11d0bd4.

📒 Files selected for processing (6)
  • benchmarks/client-nav/react/app.tsx
  • benchmarks/client-nav/react/speed.bench.tsx
  • benchmarks/client-nav/solid/app.tsx
  • benchmarks/client-nav/solid/speed.bench.tsx
  • benchmarks/client-nav/vue/app.tsx
  • benchmarks/client-nav/vue/speed.bench.tsx

@codspeed-hq
Copy link

codspeed-hq bot commented Mar 3, 2026

Merging this PR will degrade performance by 16.32%

❌ 2 regressed benchmarks
✅ 3 untouched benchmarks

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
client-side navigation loop (vue) 78.4 ms 93.7 ms -16.32%
client-side navigation loop (react) 98.5 ms 116.4 ms -15.32%

Comparing test-benchmarks-client-nav-more-representative-app (016e842) with main (2217f7c)

Open in CodSpeed

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

🧹 Nitpick comments (1)
benchmarks/client-nav/solid/app.tsx (1)

78-78: Type safety: validateSearch accesses search.id without type narrowing.

The search parameter in validateSearch is Record<string, unknown> by default. Accessing search.id directly may cause TypeScript errors in strict mode since the value is unknown. Consider adding type assertion or proper validation.

✏️ Proposed fix with type assertion
-  validateSearch: (search) => ({ id: search.id }),
+  validateSearch: (search: Record<string, unknown>) => ({ id: search.id as string }),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@benchmarks/client-nav/solid/app.tsx` at line 78, The validateSearch
implementation uses search.id on a parameter typed as Record<string, unknown>,
causing TypeScript errors; update validateSearch (the function named
validateSearch) to narrow or assert the type of search before accessing .id —
either add a runtime guard that checks search is an object and that 'id' in
search and typeof (search['id']) === 'string' (or the expected type) before
returning { id: ... }, or change the function signature to accept a properly
typed parameter (e.g., { id: string } or a typed interface) and adjust callers
accordingly so access to search.id is type-safe.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@benchmarks/client-nav/solid/app.tsx`:
- Around line 43-49: The select callback in Match is accessing match.params.id
but with strict: false match.params can be undefined; update the select to use
optional chaining (e.g. select: (match) =>
runPerfSelectorComputation(Number(match.params?.id ?? 0))) so params is safely
accessed, and ensure any other direct uses of match.params in Match use the same
match.params?.id pattern; keep using runPerfSelectorComputation and the number()
call as-is.

---

Nitpick comments:
In `@benchmarks/client-nav/solid/app.tsx`:
- Line 78: The validateSearch implementation uses search.id on a parameter typed
as Record<string, unknown>, causing TypeScript errors; update validateSearch
(the function named validateSearch) to narrow or assert the type of search
before accessing .id — either add a runtime guard that checks search is an
object and that 'id' in search and typeof (search['id']) === 'string' (or the
expected type) before returning { id: ... }, or change the function signature to
accept a properly typed parameter (e.g., { id: string } or a typed interface)
and adjust callers accordingly so access to search.id is type-safe.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 11d0bd4 and 016e842.

📒 Files selected for processing (1)
  • benchmarks/client-nav/solid/app.tsx

Comment on lines +43 to 49
function Match() {
const number = useMatch({
strict: false,
select: (match) => runPerfSelectorComputation(Number(match.params.id ?? 0)),
})

return null
return <>{runPerfSelectorComputation(number())}</>
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n benchmarks/client-nav/solid/app.tsx | head -60

Repository: TanStack/router

Length of output: 1748


🌐 Web query:

TanStack Router useMatch strict false select callback documentation

💡 Result:

TanStack Router documents useMatch’s strict and select options on the useMatch hook API page. (tanstack.com)

strict: false

  • Default is true.
  • If strict is false, you must not pass from (it must be unset), and the returned match type is loosened (a Partial<RouteMatch>-style shape) to reflect that it could be any active match. (tanstack.com)
  • This is intended for shared/reusable components where you don’t know which route you’re under (also described in the Type Safety guide). (tanstack.com)

Example:

const match = useMatch({ strict: false })

(tanstack.com)

select: (match) => TSelected (the “select callback”)

  • Optional function called with the RouteMatch.
  • useMatch returns whatever your select returns.
  • The selected value is also used to decide whether to re-render (via shallow equality checks). (tanstack.com)

Example:

const pathname = useMatch({
  strict: false,
  select: (match) => match.pathname,
})

(Example pattern referenced in community discussion; core behavior is in the docs.) (tanstack.com)

Citations:


🏁 Script executed:

rg "strict.*false" benchmarks/client-nav/solid/app.tsx -A 3

Repository: TanStack/router

Length of output: 528


🌐 Web query:

TanStack Solid Router useMatch RouteMatch Partial type strict false

💡 Result:

In TanStack Router for Solid (@tanstack/solid-router), this is expected behavior:

  • useMatch is strict by default (strict: true). In strict mode you should pass from (a route id/path), and you get a fully-typed RouteMatch. (tanstack.com)
  • If you set strict: false, you must NOT pass from, and the return type becomes Partial<RouteMatch> (so properties like search are optional / possibly undefined). This is by design for reusable/shared components. (tanstack.com)

What to do

1) Route-specific component (fully typed)

const match = useMatch({ from: Route.fullPath }) // or Route.id
// match.search is fully typed

(tanstack.com)

2) Reusable component across many routes (no invariant, looser types)

const match = useMatch({ strict: false })
// match is Partial<RouteMatch>, so match.search may be undefined

(tanstack.com)

3) If you don’t want it to throw when a specific match isn’t active

const match = useMatch({ from: '/posts', shouldThrow: false }) // RouteMatch | undefined

(tanstack.com)

Citations:


🏁 Script executed:

# Search for type definitions or documentation in the repo
fd -e ts -e tsx | xargs rg "Partial.*RouteMatch" | head -20

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

# Check for tsconfig and strict mode settings
fd -e json -name "tsconfig*" | xargs cat | grep -A 10 '"strict"' || echo "No strict setting found"

Repository: TanStack/router

Length of output: 330


🏁 Script executed:

# Check for any type errors or eslint configs
fd -e json -name ".eslintrc*" -o -name "tsconfig*" benchmarks/ | head -5

Repository: TanStack/router

Length of output: 289


🏁 Script executed:

# Look for type definition or implementation of useMatch in solid-router
fd "@tanstack/solid-router" -t d | head -3

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

find . -name "tsconfig*.json" -type f 2>/dev/null | head -10

Repository: TanStack/router

Length of output: 435


🏁 Script executed:

# Look for TypeScript configuration
cat tsconfig.json 2>/dev/null | head -30 || echo "No root tsconfig found"

Repository: TanStack/router

Length of output: 846


🏁 Script executed:

# Check if there's a type definition file in node_modules or packages
find . -path "*/node_modules/@tanstack/solid-router*" -name "*.d.ts" 2>/dev/null | head -5 || echo "Type definitions not found in sandbox"

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

# Search for type definitions within the repo itself
find . -path "*/packages/solid-router*" -type f \( -name "*.ts" -o -name "*.tsx" \) | grep -i "use-match\|useMatch" | head -10

Repository: TanStack/router

Length of output: 197


🏁 Script executed:

cat benchmarks/client-nav/solid/tsconfig.json

Repository: TanStack/router

Length of output: 315


🏁 Script executed:

cat packages/solid-router/src/useMatch.tsx

Repository: TanStack/router

Length of output: 3495


Use optional chaining to safely access match.params with strict: false.

With strict: false, the useMatch hook types the match parameter in the select callback as a partial/union type (MakeRouteMatchUnion), making the params property optional. Accessing match.params.id directly will fail TypeScript strict mode type checking. Use optional chaining to safely handle the potentially undefined params property:

 function Match() {
   const number = useMatch({
     strict: false,
-    select: (match) => runPerfSelectorComputation(Number(match.params.id ?? 0)),
+    select: (match) => runPerfSelectorComputation(Number(match?.params?.id ?? 0)),
   })
   return <>{runPerfSelectorComputation(number())}</>
 }
📝 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
function Match() {
const number = useMatch({
strict: false,
select: (match) => runPerfSelectorComputation(Number(match.params.id ?? 0)),
})
return null
return <>{runPerfSelectorComputation(number())}</>
}
function Match() {
const number = useMatch({
strict: false,
select: (match) => runPerfSelectorComputation(Number(match?.params?.id ?? 0)),
})
return <>{runPerfSelectorComputation(number())}</>
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@benchmarks/client-nav/solid/app.tsx` around lines 43 - 49, The select
callback in Match is accessing match.params.id but with strict: false
match.params can be undefined; update the select to use optional chaining (e.g.
select: (match) => runPerfSelectorComputation(Number(match.params?.id ?? 0))) so
params is safely accessed, and ensure any other direct uses of match.params in
Match use the same match.params?.id pattern; keep using
runPerfSelectorComputation and the number() call as-is.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant