perf(router-core): guard default search parser against throws - #7665

Closed
anonrig wants to merge 1 commit into
TanStack:mainfrom
anonrig:perf/default-search-parse-guard
Closed

perf(router-core): guard default search parser against throws#7665
anonrig wants to merge 1 commit into
TanStack:mainfrom
anonrig:perf/default-search-parse-guard

Conversation

@anonrig

@anonriganonrig commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

What

Stop the default search parser from throwing a SyntaxError for every plain-string param. In packages/router-core/src/searchParams.ts, defaultParseSearch ran JSON.parse on every leftover string value:

-export const defaultParseSearch = parseSearchWith(JSON.parse)+export const defaultParseSearch = parseSearchWith(parseMaybeJson)

parseMaybeJson first checks, from the first non-whitespace character, whether the value could be JSON and only then calls JSON.parse. The check is a superset of JSON's value-start grammar ({ [ " -0-9t f n, skipping leading whitespace), so any string JSON.parse would accept still gets parsed — we only skip the doomed parse of plain strings.

Why

qss.decode already coerces numbers/booleans, so JSON.parse only ever runs on the remaining string values — which on real apps are mostly plain text (?q=hello&f=live&src=typed_query). Each one throws a SyntaxError that parseSearchWith catches to keep the raw string, and constructing + throwing that error (with stack capture) is the dominant cost.

Search params are parsed on every SSR request and every client navigation. In-situ before/after on the real defaultParseSearch (Vitest bench):

Query shapeold (JSON.parse)new (guarded)Speedup
mixed X-like (?q=…&f=live&src=…&pf=1)317K ops/s996K ops/s3.1×
all plain strings (?q=from:elon since:2024&f=live&…)107K ops/s176K ops/s1.7×

Correctness

Behavior is identical. The guard never skips a value JSON.parse would accept, and for skipped values it returns the raw string — exactly what parseSearchWith does today when JSON.parse throws.

Verified by:

  • The full existing searchParams suite (isomorphism round-trips + "alien deserialization" of human-typed params).
  • A 5000+ case fuzz confirming couldBeJson is a correct superset of JSON.parse's accept set.
  • Four added tests: plain-string params stay strings, leading-whitespace JSON values still parse (guard skips whitespace), JSON-looking-but-invalid values fall back to the raw string, and t/f/n-prefixed non-literals (tweet, false_alarm, null_island) stay strings.
nx run @tanstack/router-core:test:unit -> 1182 passed (+ 3 expected-fail)
nx run @tanstack/router-core:test:types -> no errors

Notes

  • Only the default parse path changes. defaultStringifySearch uses the same JSON.parse-as-throw-probe pattern when building URLs (also hot via <Link>), but its logic relies on throw-vs-no-throw as the "is this JSON?" signal, so making it allocation-free cleanly is a slightly larger change — happy to do it as a follow-up.
  • Includes a changeset (@tanstack/router-core patch).
  • Pre-existing unused-imports/no-unused-vars warnings on the existing catch (_err) blocks are untouched by this change.

Summary by CodeRabbit

  • Performance
    • Optimized query parameter parsing to reduce processing overhead, improving performance for server-side rendering and client-side navigation.

defaultParseSearch ran JSON.parse on every leftover string search value.
For plain strings (the common case, e.g. ?q=hello&f=live) JSON.parse
throws a SyntaxError that parseSearchWith catches to keep the raw string —
and constructing/throwing that error is the dominant cost.
Add a couldBeJson guard that decides from the first non-whitespace
character whether a value could be valid JSON. It is a superset of JSON's
value-start grammar, so any string JSON.parse would accept still gets
parsed (identical results); we only skip the guaranteed-throwing parse of
non-JSON strings.
Search params are parsed on every SSR request and every client
navigation. In-situ before/after on defaultParseSearch: ~3.1x faster on
mixed X-like queries, ~1.7x on an all-plain-string query.
@coderabbitai

coderabbitaiBot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 81d1c0ab-ecfc-4d71-a1b9-8028d3b188c0

📥 Commits

Reviewing files that changed from the base of the PR and between 279a849 and b4a3303.

📒 Files selected for processing (3)
  • .changeset/perf-default-search-parse-guard.md
  • packages/router-core/src/searchParams.ts
  • packages/router-core/tests/searchParams.test.ts

📝 Walkthrough

Walkthrough

Adds two internal helpers (couldBeJson and parseMaybeJson) to searchParams.ts. couldBeJson checks the first non-whitespace character to determine if a string could be a valid JSON value; parseMaybeJson calls JSON.parse only when that check passes. defaultParseSearch is updated to use parseMaybeJson instead of JSON.parse directly. New tests and a changeset accompany the change.

Changes

defaultParseSearch JSON parse guard

Layer / File(s)Summary
couldBeJson / parseMaybeJson helpers and defaultParseSearch wiring
packages/router-core/src/searchParams.ts, packages/router-core/tests/searchParams.test.ts, .changeset/perf-default-search-parse-guard.md
couldBeJson scans past leading whitespace and checks the first character against valid JSON-start characters. parseMaybeJson wraps JSON.parse behind that guard, returning the raw string when the check fails. defaultParseSearch is switched from parseSearchWith(JSON.parse) to parseSearchWith(parseMaybeJson). Tests cover plain strings, leading-whitespace JSON, invalid JSON-looking inputs, and non-literal t/f/n-prefixed words. The changeset documents the performance motivation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Suggested labels

package: router-core

Suggested reviewers

  • chorobin

Poem

🐇 Hop hop, no parse for thee,
A string that starts with h runs free!
No more throw/catch on every quest,
couldBeJson guards the nest.
Less overhead, more speedy ways—
The router hops through query haze! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'perf(router-core): guard default search parser against throws' directly and specifically describes the main change—adding a guard to prevent expensive JSON.parse throws in the default search parser, which is the core optimization in this PR.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

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

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

❤️ Share

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

@nx-cloud

nx-cloudBot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit b4a3303

CommandStatusDurationResult
nx affected --targets=test:eslint,test:unit,tes...❌ Failed22m 33sView ↗
nx run-many --target=build --exclude=examples/*...✅ Succeeded2m 17sView ↗

☁️ Nx Cloud last updated this comment at 2026-06-22 13:53:24 UTC

@pkg-pr-new

Copy link
Copy Markdown
More templates

@tanstack/arktype-adapter

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

@tanstack/eslint-plugin-router

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

@tanstack/eslint-plugin-start

npm i https://pkg.pr.new/@tanstack/eslint-plugin-start@7665

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-rsc

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-fn-stubs

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

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

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: b4a3303

@codspeed-hq

Copy link
Copy Markdown

Merging this PR will degrade performance by 11.83%

❌ 3 regressed benchmarks
✅ 141 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

ModeBenchmarkBASEHEADEfficiency
Memorymem serialization-payload (vue)6.8 MB8.7 MB-22.2%
Memorymem aborted-requests (vue)959.1 KB1,048.9 KB-8.56%
Memorymem request-churn (vue)1.3 MB1.3 MB-3.66%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing anonrig:perf/default-search-parse-guard (b4a3303) with main (f23ed0f)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (279a849) during the generation of this report, so f23ed0f was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@nx-cloudnx-cloudBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nx Cloud has identified a possible root cause for your failed CI:

We classified this failure as an environment state issue rather than a code change. The error — a missing resolveRuntimeSuffix export in e2e/e2e-utils/dist/esm/index.js — points to stale or inconsistently built dist artifacts in the e2e test infrastructure, which has no relation to the @tanstack/router-core search param optimization introduced by this PR. Rebuilding the e2e-utils package should restore consistent artifacts and resolve the failure.

No code changes were suggested for this issue.

You can trigger a rerun by pushing an empty commit:

git commit --allow-empty -m "chore: trigger rerun"
git push

Nx Cloud View detailed reasoning on Nx Cloud ↗


🎓 Learn more about Self-Healing CI on nx.dev

@anonrig

Copy link
Copy Markdown
ContributorAuthor

I'm closing this because Tanstack router repository has a limit of maximum number of pull-request open per a user and it is 6!!!!!

@anonriganonrig closed this Jun 22, 2026
@Sheraff

Copy link
Copy Markdown
Collaborator

I'm closing this because Tanstack router repository has a limit of maximum number of pull-request open per a user and it is 6!!!!!

I already had a pretty similar PR a while ago https://github.com/TanStack/router/pull/5071/changes

not sure why we never merged it.

Sign up for freeto 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.

2 participants

@anonrig@Sheraff
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

perf(router-core): guard default search parser against throws - #7665

Closed
anonrig wants to merge 1 commit into
TanStack:mainfrom
anonrig:perf/default-search-parse-guard
Closed

perf(router-core): guard default search parser against throws#7665
anonrig wants to merge 1 commit into
TanStack:mainfrom
anonrig:perf/default-search-parse-guard

Conversation

@anonrig

@anonriganonrig commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

What

Stop the default search parser from throwing a SyntaxError for every plain-string param. In packages/router-core/src/searchParams.ts, defaultParseSearch ran JSON.parse on every leftover string value:

-export const defaultParseSearch = parseSearchWith(JSON.parse)+export const defaultParseSearch = parseSearchWith(parseMaybeJson)

parseMaybeJson first checks, from the first non-whitespace character, whether the value could be JSON and only then calls JSON.parse. The check is a superset of JSON's value-start grammar ({ [ " -0-9t f n, skipping leading whitespace), so any string JSON.parse would accept still gets parsed — we only skip the doomed parse of plain strings.

Why

qss.decode already coerces numbers/booleans, so JSON.parse only ever runs on the remaining string values — which on real apps are mostly plain text (?q=hello&f=live&src=typed_query). Each one throws a SyntaxError that parseSearchWith catches to keep the raw string, and constructing + throwing that error (with stack capture) is the dominant cost.

Search params are parsed on every SSR request and every client navigation. In-situ before/after on the real defaultParseSearch (Vitest bench):

Query shapeold (JSON.parse)new (guarded)Speedup
mixed X-like (?q=…&f=live&src=…&pf=1)317K ops/s996K ops/s3.1×
all plain strings (?q=from:elon since:2024&f=live&…)107K ops/s176K ops/s1.7×

Correctness

Behavior is identical. The guard never skips a value JSON.parse would accept, and for skipped values it returns the raw string — exactly what parseSearchWith does today when JSON.parse throws.

Verified by:

  • The full existing searchParams suite (isomorphism round-trips + "alien deserialization" of human-typed params).
  • A 5000+ case fuzz confirming couldBeJson is a correct superset of JSON.parse's accept set.
  • Four added tests: plain-string params stay strings, leading-whitespace JSON values still parse (guard skips whitespace), JSON-looking-but-invalid values fall back to the raw string, and t/f/n-prefixed non-literals (tweet, false_alarm, null_island) stay strings.
nx run @tanstack/router-core:test:unit -> 1182 passed (+ 3 expected-fail)
nx run @tanstack/router-core:test:types -> no errors

Notes

  • Only the default parse path changes. defaultStringifySearch uses the same JSON.parse-as-throw-probe pattern when building URLs (also hot via <Link>), but its logic relies on throw-vs-no-throw as the "is this JSON?" signal, so making it allocation-free cleanly is a slightly larger change — happy to do it as a follow-up.
  • Includes a changeset (@tanstack/router-core patch).
  • Pre-existing unused-imports/no-unused-vars warnings on the existing catch (_err) blocks are untouched by this change.

Summary by CodeRabbit

  • Performance
    • Optimized query parameter parsing to reduce processing overhead, improving performance for server-side rendering and client-side navigation.

defaultParseSearch ran JSON.parse on every leftover string search value.
For plain strings (the common case, e.g. ?q=hello&f=live) JSON.parse
throws a SyntaxError that parseSearchWith catches to keep the raw string —
and constructing/throwing that error is the dominant cost.
Add a couldBeJson guard that decides from the first non-whitespace
character whether a value could be valid JSON. It is a superset of JSON's
value-start grammar, so any string JSON.parse would accept still gets
parsed (identical results); we only skip the guaranteed-throwing parse of
non-JSON strings.
Search params are parsed on every SSR request and every client
navigation. In-situ before/after on defaultParseSearch: ~3.1x faster on
mixed X-like queries, ~1.7x on an all-plain-string query.
@coderabbitai

coderabbitaiBot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 81d1c0ab-ecfc-4d71-a1b9-8028d3b188c0

📥 Commits

Reviewing files that changed from the base of the PR and between 279a849 and b4a3303.

📒 Files selected for processing (3)
  • .changeset/perf-default-search-parse-guard.md
  • packages/router-core/src/searchParams.ts
  • packages/router-core/tests/searchParams.test.ts

📝 Walkthrough

Walkthrough

Adds two internal helpers (couldBeJson and parseMaybeJson) to searchParams.ts. couldBeJson checks the first non-whitespace character to determine if a string could be a valid JSON value; parseMaybeJson calls JSON.parse only when that check passes. defaultParseSearch is updated to use parseMaybeJson instead of JSON.parse directly. New tests and a changeset accompany the change.

Changes

defaultParseSearch JSON parse guard

Layer / File(s)Summary
couldBeJson / parseMaybeJson helpers and defaultParseSearch wiring
packages/router-core/src/searchParams.ts, packages/router-core/tests/searchParams.test.ts, .changeset/perf-default-search-parse-guard.md
couldBeJson scans past leading whitespace and checks the first character against valid JSON-start characters. parseMaybeJson wraps JSON.parse behind that guard, returning the raw string when the check fails. defaultParseSearch is switched from parseSearchWith(JSON.parse) to parseSearchWith(parseMaybeJson). Tests cover plain strings, leading-whitespace JSON, invalid JSON-looking inputs, and non-literal t/f/n-prefixed words. The changeset documents the performance motivation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Suggested labels

package: router-core

Suggested reviewers

  • chorobin

Poem

🐇 Hop hop, no parse for thee,
A string that starts with h runs free!
No more throw/catch on every quest,
couldBeJson guards the nest.
Less overhead, more speedy ways—
The router hops through query haze! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'perf(router-core): guard default search parser against throws' directly and specifically describes the main change—adding a guard to prevent expensive JSON.parse throws in the default search parser, which is the core optimization in this PR.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

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

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

❤️ Share

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

@nx-cloud

nx-cloudBot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit b4a3303

CommandStatusDurationResult
nx affected --targets=test:eslint,test:unit,tes...❌ Failed22m 33sView ↗
nx run-many --target=build --exclude=examples/*...✅ Succeeded2m 17sView ↗

☁️ Nx Cloud last updated this comment at 2026-06-22 13:53:24 UTC

@pkg-pr-new

Copy link
Copy Markdown
More templates

@tanstack/arktype-adapter

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

@tanstack/eslint-plugin-router

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

@tanstack/eslint-plugin-start

npm i https://pkg.pr.new/@tanstack/eslint-plugin-start@7665

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-rsc

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-fn-stubs

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

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

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: b4a3303

@codspeed-hq

Copy link
Copy Markdown

Merging this PR will degrade performance by 11.83%

❌ 3 regressed benchmarks
✅ 141 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

ModeBenchmarkBASEHEADEfficiency
Memorymem serialization-payload (vue)6.8 MB8.7 MB-22.2%
Memorymem aborted-requests (vue)959.1 KB1,048.9 KB-8.56%
Memorymem request-churn (vue)1.3 MB1.3 MB-3.66%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing anonrig:perf/default-search-parse-guard (b4a3303) with main (f23ed0f)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (279a849) during the generation of this report, so f23ed0f was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@nx-cloudnx-cloudBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nx Cloud has identified a possible root cause for your failed CI:

We classified this failure as an environment state issue rather than a code change. The error — a missing resolveRuntimeSuffix export in e2e/e2e-utils/dist/esm/index.js — points to stale or inconsistently built dist artifacts in the e2e test infrastructure, which has no relation to the @tanstack/router-core search param optimization introduced by this PR. Rebuilding the e2e-utils package should restore consistent artifacts and resolve the failure.

No code changes were suggested for this issue.

You can trigger a rerun by pushing an empty commit:

git commit --allow-empty -m "chore: trigger rerun"
git push

Nx Cloud View detailed reasoning on Nx Cloud ↗


🎓 Learn more about Self-Healing CI on nx.dev

@anonrig

Copy link
Copy Markdown
ContributorAuthor

I'm closing this because Tanstack router repository has a limit of maximum number of pull-request open per a user and it is 6!!!!!

@anonriganonrig closed this Jun 22, 2026
@Sheraff

Copy link
Copy Markdown
Collaborator

I'm closing this because Tanstack router repository has a limit of maximum number of pull-request open per a user and it is 6!!!!!

I already had a pretty similar PR a while ago https://github.com/TanStack/router/pull/5071/changes

not sure why we never merged it.

Sign up for freeto 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.

2 participants

@anonrig@Sheraff
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

perf(router-core): guard default search parser against throws - #7665

Closed
anonrig wants to merge 1 commit into
TanStack:mainfrom
anonrig:perf/default-search-parse-guard
Closed

perf(router-core): guard default search parser against throws#7665
anonrig wants to merge 1 commit into
TanStack:mainfrom
anonrig:perf/default-search-parse-guard

Conversation

@anonrig

@anonriganonrig commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

What

Stop the default search parser from throwing a SyntaxError for every plain-string param. In packages/router-core/src/searchParams.ts, defaultParseSearch ran JSON.parse on every leftover string value:

-export const defaultParseSearch = parseSearchWith(JSON.parse)+export const defaultParseSearch = parseSearchWith(parseMaybeJson)

parseMaybeJson first checks, from the first non-whitespace character, whether the value could be JSON and only then calls JSON.parse. The check is a superset of JSON's value-start grammar ({ [ " -0-9t f n, skipping leading whitespace), so any string JSON.parse would accept still gets parsed — we only skip the doomed parse of plain strings.

Why

qss.decode already coerces numbers/booleans, so JSON.parse only ever runs on the remaining string values — which on real apps are mostly plain text (?q=hello&f=live&src=typed_query). Each one throws a SyntaxError that parseSearchWith catches to keep the raw string, and constructing + throwing that error (with stack capture) is the dominant cost.

Search params are parsed on every SSR request and every client navigation. In-situ before/after on the real defaultParseSearch (Vitest bench):

Query shapeold (JSON.parse)new (guarded)Speedup
mixed X-like (?q=…&f=live&src=…&pf=1)317K ops/s996K ops/s3.1×
all plain strings (?q=from:elon since:2024&f=live&…)107K ops/s176K ops/s1.7×

Correctness

Behavior is identical. The guard never skips a value JSON.parse would accept, and for skipped values it returns the raw string — exactly what parseSearchWith does today when JSON.parse throws.

Verified by:

  • The full existing searchParams suite (isomorphism round-trips + "alien deserialization" of human-typed params).
  • A 5000+ case fuzz confirming couldBeJson is a correct superset of JSON.parse's accept set.
  • Four added tests: plain-string params stay strings, leading-whitespace JSON values still parse (guard skips whitespace), JSON-looking-but-invalid values fall back to the raw string, and t/f/n-prefixed non-literals (tweet, false_alarm, null_island) stay strings.
nx run @tanstack/router-core:test:unit -> 1182 passed (+ 3 expected-fail)
nx run @tanstack/router-core:test:types -> no errors

Notes

  • Only the default parse path changes. defaultStringifySearch uses the same JSON.parse-as-throw-probe pattern when building URLs (also hot via <Link>), but its logic relies on throw-vs-no-throw as the "is this JSON?" signal, so making it allocation-free cleanly is a slightly larger change — happy to do it as a follow-up.
  • Includes a changeset (@tanstack/router-core patch).
  • Pre-existing unused-imports/no-unused-vars warnings on the existing catch (_err) blocks are untouched by this change.

Summary by CodeRabbit

  • Performance
    • Optimized query parameter parsing to reduce processing overhead, improving performance for server-side rendering and client-side navigation.

defaultParseSearch ran JSON.parse on every leftover string search value.
For plain strings (the common case, e.g. ?q=hello&f=live) JSON.parse
throws a SyntaxError that parseSearchWith catches to keep the raw string —
and constructing/throwing that error is the dominant cost.
Add a couldBeJson guard that decides from the first non-whitespace
character whether a value could be valid JSON. It is a superset of JSON's
value-start grammar, so any string JSON.parse would accept still gets
parsed (identical results); we only skip the guaranteed-throwing parse of
non-JSON strings.
Search params are parsed on every SSR request and every client
navigation. In-situ before/after on defaultParseSearch: ~3.1x faster on
mixed X-like queries, ~1.7x on an all-plain-string query.
@coderabbitai

coderabbitaiBot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 81d1c0ab-ecfc-4d71-a1b9-8028d3b188c0

📥 Commits

Reviewing files that changed from the base of the PR and between 279a849 and b4a3303.

📒 Files selected for processing (3)
  • .changeset/perf-default-search-parse-guard.md
  • packages/router-core/src/searchParams.ts
  • packages/router-core/tests/searchParams.test.ts

📝 Walkthrough

Walkthrough

Adds two internal helpers (couldBeJson and parseMaybeJson) to searchParams.ts. couldBeJson checks the first non-whitespace character to determine if a string could be a valid JSON value; parseMaybeJson calls JSON.parse only when that check passes. defaultParseSearch is updated to use parseMaybeJson instead of JSON.parse directly. New tests and a changeset accompany the change.

Changes

defaultParseSearch JSON parse guard

Layer / File(s)Summary
couldBeJson / parseMaybeJson helpers and defaultParseSearch wiring
packages/router-core/src/searchParams.ts, packages/router-core/tests/searchParams.test.ts, .changeset/perf-default-search-parse-guard.md
couldBeJson scans past leading whitespace and checks the first character against valid JSON-start characters. parseMaybeJson wraps JSON.parse behind that guard, returning the raw string when the check fails. defaultParseSearch is switched from parseSearchWith(JSON.parse) to parseSearchWith(parseMaybeJson). Tests cover plain strings, leading-whitespace JSON, invalid JSON-looking inputs, and non-literal t/f/n-prefixed words. The changeset documents the performance motivation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Suggested labels

package: router-core

Suggested reviewers

  • chorobin

Poem

🐇 Hop hop, no parse for thee,
A string that starts with h runs free!
No more throw/catch on every quest,
couldBeJson guards the nest.
Less overhead, more speedy ways—
The router hops through query haze! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'perf(router-core): guard default search parser against throws' directly and specifically describes the main change—adding a guard to prevent expensive JSON.parse throws in the default search parser, which is the core optimization in this PR.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

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

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

❤️ Share

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

@nx-cloud

nx-cloudBot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit b4a3303

CommandStatusDurationResult
nx affected --targets=test:eslint,test:unit,tes...❌ Failed22m 33sView ↗
nx run-many --target=build --exclude=examples/*...✅ Succeeded2m 17sView ↗

☁️ Nx Cloud last updated this comment at 2026-06-22 13:53:24 UTC

@pkg-pr-new

Copy link
Copy Markdown
More templates

@tanstack/arktype-adapter

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

@tanstack/eslint-plugin-router

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

@tanstack/eslint-plugin-start

npm i https://pkg.pr.new/@tanstack/eslint-plugin-start@7665

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-rsc

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-fn-stubs

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

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

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: b4a3303

@codspeed-hq

Copy link
Copy Markdown

Merging this PR will degrade performance by 11.83%

❌ 3 regressed benchmarks
✅ 141 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

ModeBenchmarkBASEHEADEfficiency
Memorymem serialization-payload (vue)6.8 MB8.7 MB-22.2%
Memorymem aborted-requests (vue)959.1 KB1,048.9 KB-8.56%
Memorymem request-churn (vue)1.3 MB1.3 MB-3.66%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing anonrig:perf/default-search-parse-guard (b4a3303) with main (f23ed0f)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (279a849) during the generation of this report, so f23ed0f was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@nx-cloudnx-cloudBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nx Cloud has identified a possible root cause for your failed CI:

We classified this failure as an environment state issue rather than a code change. The error — a missing resolveRuntimeSuffix export in e2e/e2e-utils/dist/esm/index.js — points to stale or inconsistently built dist artifacts in the e2e test infrastructure, which has no relation to the @tanstack/router-core search param optimization introduced by this PR. Rebuilding the e2e-utils package should restore consistent artifacts and resolve the failure.

No code changes were suggested for this issue.

You can trigger a rerun by pushing an empty commit:

git commit --allow-empty -m "chore: trigger rerun"
git push

Nx Cloud View detailed reasoning on Nx Cloud ↗


🎓 Learn more about Self-Healing CI on nx.dev

@anonrig

Copy link
Copy Markdown
ContributorAuthor

I'm closing this because Tanstack router repository has a limit of maximum number of pull-request open per a user and it is 6!!!!!

@anonriganonrig closed this Jun 22, 2026
@Sheraff

Copy link
Copy Markdown
Collaborator

I'm closing this because Tanstack router repository has a limit of maximum number of pull-request open per a user and it is 6!!!!!

I already had a pretty similar PR a while ago https://github.com/TanStack/router/pull/5071/changes

not sure why we never merged it.

Sign up for freeto 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.

2 participants

@anonrig@Sheraff
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

perf(router-core): guard default search parser against throws - #7665

Closed
anonrig wants to merge 1 commit into
TanStack:mainfrom
anonrig:perf/default-search-parse-guard
Closed

perf(router-core): guard default search parser against throws#7665
anonrig wants to merge 1 commit into
TanStack:mainfrom
anonrig:perf/default-search-parse-guard

Conversation

@anonrig

@anonriganonrig commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

What

Stop the default search parser from throwing a SyntaxError for every plain-string param. In packages/router-core/src/searchParams.ts, defaultParseSearch ran JSON.parse on every leftover string value:

-export const defaultParseSearch = parseSearchWith(JSON.parse)+export const defaultParseSearch = parseSearchWith(parseMaybeJson)

parseMaybeJson first checks, from the first non-whitespace character, whether the value could be JSON and only then calls JSON.parse. The check is a superset of JSON's value-start grammar ({ [ " -0-9t f n, skipping leading whitespace), so any string JSON.parse would accept still gets parsed — we only skip the doomed parse of plain strings.

Why

qss.decode already coerces numbers/booleans, so JSON.parse only ever runs on the remaining string values — which on real apps are mostly plain text (?q=hello&f=live&src=typed_query). Each one throws a SyntaxError that parseSearchWith catches to keep the raw string, and constructing + throwing that error (with stack capture) is the dominant cost.

Search params are parsed on every SSR request and every client navigation. In-situ before/after on the real defaultParseSearch (Vitest bench):

Query shapeold (JSON.parse)new (guarded)Speedup
mixed X-like (?q=…&f=live&src=…&pf=1)317K ops/s996K ops/s3.1×
all plain strings (?q=from:elon since:2024&f=live&…)107K ops/s176K ops/s1.7×

Correctness

Behavior is identical. The guard never skips a value JSON.parse would accept, and for skipped values it returns the raw string — exactly what parseSearchWith does today when JSON.parse throws.

Verified by:

  • The full existing searchParams suite (isomorphism round-trips + "alien deserialization" of human-typed params).
  • A 5000+ case fuzz confirming couldBeJson is a correct superset of JSON.parse's accept set.
  • Four added tests: plain-string params stay strings, leading-whitespace JSON values still parse (guard skips whitespace), JSON-looking-but-invalid values fall back to the raw string, and t/f/n-prefixed non-literals (tweet, false_alarm, null_island) stay strings.
nx run @tanstack/router-core:test:unit -> 1182 passed (+ 3 expected-fail)
nx run @tanstack/router-core:test:types -> no errors

Notes

  • Only the default parse path changes. defaultStringifySearch uses the same JSON.parse-as-throw-probe pattern when building URLs (also hot via <Link>), but its logic relies on throw-vs-no-throw as the "is this JSON?" signal, so making it allocation-free cleanly is a slightly larger change — happy to do it as a follow-up.
  • Includes a changeset (@tanstack/router-core patch).
  • Pre-existing unused-imports/no-unused-vars warnings on the existing catch (_err) blocks are untouched by this change.

Summary by CodeRabbit

  • Performance
    • Optimized query parameter parsing to reduce processing overhead, improving performance for server-side rendering and client-side navigation.

defaultParseSearch ran JSON.parse on every leftover string search value.
For plain strings (the common case, e.g. ?q=hello&f=live) JSON.parse
throws a SyntaxError that parseSearchWith catches to keep the raw string —
and constructing/throwing that error is the dominant cost.
Add a couldBeJson guard that decides from the first non-whitespace
character whether a value could be valid JSON. It is a superset of JSON's
value-start grammar, so any string JSON.parse would accept still gets
parsed (identical results); we only skip the guaranteed-throwing parse of
non-JSON strings.
Search params are parsed on every SSR request and every client
navigation. In-situ before/after on defaultParseSearch: ~3.1x faster on
mixed X-like queries, ~1.7x on an all-plain-string query.
@coderabbitai

coderabbitaiBot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 81d1c0ab-ecfc-4d71-a1b9-8028d3b188c0

📥 Commits

Reviewing files that changed from the base of the PR and between 279a849 and b4a3303.

📒 Files selected for processing (3)
  • .changeset/perf-default-search-parse-guard.md
  • packages/router-core/src/searchParams.ts
  • packages/router-core/tests/searchParams.test.ts

📝 Walkthrough

Walkthrough

Adds two internal helpers (couldBeJson and parseMaybeJson) to searchParams.ts. couldBeJson checks the first non-whitespace character to determine if a string could be a valid JSON value; parseMaybeJson calls JSON.parse only when that check passes. defaultParseSearch is updated to use parseMaybeJson instead of JSON.parse directly. New tests and a changeset accompany the change.

Changes

defaultParseSearch JSON parse guard

Layer / File(s)Summary
couldBeJson / parseMaybeJson helpers and defaultParseSearch wiring
packages/router-core/src/searchParams.ts, packages/router-core/tests/searchParams.test.ts, .changeset/perf-default-search-parse-guard.md
couldBeJson scans past leading whitespace and checks the first character against valid JSON-start characters. parseMaybeJson wraps JSON.parse behind that guard, returning the raw string when the check fails. defaultParseSearch is switched from parseSearchWith(JSON.parse) to parseSearchWith(parseMaybeJson). Tests cover plain strings, leading-whitespace JSON, invalid JSON-looking inputs, and non-literal t/f/n-prefixed words. The changeset documents the performance motivation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Suggested labels

package: router-core

Suggested reviewers

  • chorobin

Poem

🐇 Hop hop, no parse for thee,
A string that starts with h runs free!
No more throw/catch on every quest,
couldBeJson guards the nest.
Less overhead, more speedy ways—
The router hops through query haze! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'perf(router-core): guard default search parser against throws' directly and specifically describes the main change—adding a guard to prevent expensive JSON.parse throws in the default search parser, which is the core optimization in this PR.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

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

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

❤️ Share

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

@nx-cloud

nx-cloudBot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit b4a3303

CommandStatusDurationResult
nx affected --targets=test:eslint,test:unit,tes...❌ Failed22m 33sView ↗
nx run-many --target=build --exclude=examples/*...✅ Succeeded2m 17sView ↗

☁️ Nx Cloud last updated this comment at 2026-06-22 13:53:24 UTC

@pkg-pr-new

Copy link
Copy Markdown
More templates

@tanstack/arktype-adapter

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

@tanstack/eslint-plugin-router

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

@tanstack/eslint-plugin-start

npm i https://pkg.pr.new/@tanstack/eslint-plugin-start@7665

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-rsc

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-fn-stubs

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

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

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: b4a3303

@codspeed-hq

Copy link
Copy Markdown

Merging this PR will degrade performance by 11.83%

❌ 3 regressed benchmarks
✅ 141 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

ModeBenchmarkBASEHEADEfficiency
Memorymem serialization-payload (vue)6.8 MB8.7 MB-22.2%
Memorymem aborted-requests (vue)959.1 KB1,048.9 KB-8.56%
Memorymem request-churn (vue)1.3 MB1.3 MB-3.66%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing anonrig:perf/default-search-parse-guard (b4a3303) with main (f23ed0f)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (279a849) during the generation of this report, so f23ed0f was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@nx-cloudnx-cloudBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nx Cloud has identified a possible root cause for your failed CI:

We classified this failure as an environment state issue rather than a code change. The error — a missing resolveRuntimeSuffix export in e2e/e2e-utils/dist/esm/index.js — points to stale or inconsistently built dist artifacts in the e2e test infrastructure, which has no relation to the @tanstack/router-core search param optimization introduced by this PR. Rebuilding the e2e-utils package should restore consistent artifacts and resolve the failure.

No code changes were suggested for this issue.

You can trigger a rerun by pushing an empty commit:

git commit --allow-empty -m "chore: trigger rerun"
git push

Nx Cloud View detailed reasoning on Nx Cloud ↗


🎓 Learn more about Self-Healing CI on nx.dev

@anonrig

Copy link
Copy Markdown
ContributorAuthor

I'm closing this because Tanstack router repository has a limit of maximum number of pull-request open per a user and it is 6!!!!!

@anonriganonrig closed this Jun 22, 2026
@Sheraff

Copy link
Copy Markdown
Collaborator

I'm closing this because Tanstack router repository has a limit of maximum number of pull-request open per a user and it is 6!!!!!

I already had a pretty similar PR a while ago https://github.com/TanStack/router/pull/5071/changes

not sure why we never merged it.

Sign up for freeto 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.

2 participants

@anonrig@Sheraff
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

perf(router-core): guard default search parser against throws - #7665

Closed
anonrig wants to merge 1 commit into
TanStack:mainfrom
anonrig:perf/default-search-parse-guard
Closed

perf(router-core): guard default search parser against throws#7665
anonrig wants to merge 1 commit into
TanStack:mainfrom
anonrig:perf/default-search-parse-guard

Conversation

@anonrig

@anonriganonrig commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

What

Stop the default search parser from throwing a SyntaxError for every plain-string param. In packages/router-core/src/searchParams.ts, defaultParseSearch ran JSON.parse on every leftover string value:

-export const defaultParseSearch = parseSearchWith(JSON.parse)+export const defaultParseSearch = parseSearchWith(parseMaybeJson)

parseMaybeJson first checks, from the first non-whitespace character, whether the value could be JSON and only then calls JSON.parse. The check is a superset of JSON's value-start grammar ({ [ " -0-9t f n, skipping leading whitespace), so any string JSON.parse would accept still gets parsed — we only skip the doomed parse of plain strings.

Why

qss.decode already coerces numbers/booleans, so JSON.parse only ever runs on the remaining string values — which on real apps are mostly plain text (?q=hello&f=live&src=typed_query). Each one throws a SyntaxError that parseSearchWith catches to keep the raw string, and constructing + throwing that error (with stack capture) is the dominant cost.

Search params are parsed on every SSR request and every client navigation. In-situ before/after on the real defaultParseSearch (Vitest bench):

Query shapeold (JSON.parse)new (guarded)Speedup
mixed X-like (?q=…&f=live&src=…&pf=1)317K ops/s996K ops/s3.1×
all plain strings (?q=from:elon since:2024&f=live&…)107K ops/s176K ops/s1.7×

Correctness

Behavior is identical. The guard never skips a value JSON.parse would accept, and for skipped values it returns the raw string — exactly what parseSearchWith does today when JSON.parse throws.

Verified by:

  • The full existing searchParams suite (isomorphism round-trips + "alien deserialization" of human-typed params).
  • A 5000+ case fuzz confirming couldBeJson is a correct superset of JSON.parse's accept set.
  • Four added tests: plain-string params stay strings, leading-whitespace JSON values still parse (guard skips whitespace), JSON-looking-but-invalid values fall back to the raw string, and t/f/n-prefixed non-literals (tweet, false_alarm, null_island) stay strings.
nx run @tanstack/router-core:test:unit -> 1182 passed (+ 3 expected-fail)
nx run @tanstack/router-core:test:types -> no errors

Notes

  • Only the default parse path changes. defaultStringifySearch uses the same JSON.parse-as-throw-probe pattern when building URLs (also hot via <Link>), but its logic relies on throw-vs-no-throw as the "is this JSON?" signal, so making it allocation-free cleanly is a slightly larger change — happy to do it as a follow-up.
  • Includes a changeset (@tanstack/router-core patch).
  • Pre-existing unused-imports/no-unused-vars warnings on the existing catch (_err) blocks are untouched by this change.

Summary by CodeRabbit

  • Performance
    • Optimized query parameter parsing to reduce processing overhead, improving performance for server-side rendering and client-side navigation.

defaultParseSearch ran JSON.parse on every leftover string search value.
For plain strings (the common case, e.g. ?q=hello&f=live) JSON.parse
throws a SyntaxError that parseSearchWith catches to keep the raw string —
and constructing/throwing that error is the dominant cost.
Add a couldBeJson guard that decides from the first non-whitespace
character whether a value could be valid JSON. It is a superset of JSON's
value-start grammar, so any string JSON.parse would accept still gets
parsed (identical results); we only skip the guaranteed-throwing parse of
non-JSON strings.
Search params are parsed on every SSR request and every client
navigation. In-situ before/after on defaultParseSearch: ~3.1x faster on
mixed X-like queries, ~1.7x on an all-plain-string query.
@coderabbitai

coderabbitaiBot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 81d1c0ab-ecfc-4d71-a1b9-8028d3b188c0

📥 Commits

Reviewing files that changed from the base of the PR and between 279a849 and b4a3303.

📒 Files selected for processing (3)
  • .changeset/perf-default-search-parse-guard.md
  • packages/router-core/src/searchParams.ts
  • packages/router-core/tests/searchParams.test.ts

📝 Walkthrough

Walkthrough

Adds two internal helpers (couldBeJson and parseMaybeJson) to searchParams.ts. couldBeJson checks the first non-whitespace character to determine if a string could be a valid JSON value; parseMaybeJson calls JSON.parse only when that check passes. defaultParseSearch is updated to use parseMaybeJson instead of JSON.parse directly. New tests and a changeset accompany the change.

Changes

defaultParseSearch JSON parse guard

Layer / File(s)Summary
couldBeJson / parseMaybeJson helpers and defaultParseSearch wiring
packages/router-core/src/searchParams.ts, packages/router-core/tests/searchParams.test.ts, .changeset/perf-default-search-parse-guard.md
couldBeJson scans past leading whitespace and checks the first character against valid JSON-start characters. parseMaybeJson wraps JSON.parse behind that guard, returning the raw string when the check fails. defaultParseSearch is switched from parseSearchWith(JSON.parse) to parseSearchWith(parseMaybeJson). Tests cover plain strings, leading-whitespace JSON, invalid JSON-looking inputs, and non-literal t/f/n-prefixed words. The changeset documents the performance motivation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Suggested labels

package: router-core

Suggested reviewers

  • chorobin

Poem

🐇 Hop hop, no parse for thee,
A string that starts with h runs free!
No more throw/catch on every quest,
couldBeJson guards the nest.
Less overhead, more speedy ways—
The router hops through query haze! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'perf(router-core): guard default search parser against throws' directly and specifically describes the main change—adding a guard to prevent expensive JSON.parse throws in the default search parser, which is the core optimization in this PR.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

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

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

❤️ Share

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

@nx-cloud

nx-cloudBot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit b4a3303

CommandStatusDurationResult
nx affected --targets=test:eslint,test:unit,tes...❌ Failed22m 33sView ↗
nx run-many --target=build --exclude=examples/*...✅ Succeeded2m 17sView ↗

☁️ Nx Cloud last updated this comment at 2026-06-22 13:53:24 UTC

@pkg-pr-new

Copy link
Copy Markdown
More templates

@tanstack/arktype-adapter

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

@tanstack/eslint-plugin-router

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

@tanstack/eslint-plugin-start

npm i https://pkg.pr.new/@tanstack/eslint-plugin-start@7665

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-rsc

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-fn-stubs

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

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

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: b4a3303

@codspeed-hq

Copy link
Copy Markdown

Merging this PR will degrade performance by 11.83%

❌ 3 regressed benchmarks
✅ 141 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

ModeBenchmarkBASEHEADEfficiency
Memorymem serialization-payload (vue)6.8 MB8.7 MB-22.2%
Memorymem aborted-requests (vue)959.1 KB1,048.9 KB-8.56%
Memorymem request-churn (vue)1.3 MB1.3 MB-3.66%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing anonrig:perf/default-search-parse-guard (b4a3303) with main (f23ed0f)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (279a849) during the generation of this report, so f23ed0f was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@nx-cloudnx-cloudBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nx Cloud has identified a possible root cause for your failed CI:

We classified this failure as an environment state issue rather than a code change. The error — a missing resolveRuntimeSuffix export in e2e/e2e-utils/dist/esm/index.js — points to stale or inconsistently built dist artifacts in the e2e test infrastructure, which has no relation to the @tanstack/router-core search param optimization introduced by this PR. Rebuilding the e2e-utils package should restore consistent artifacts and resolve the failure.

No code changes were suggested for this issue.

You can trigger a rerun by pushing an empty commit:

git commit --allow-empty -m "chore: trigger rerun"
git push

Nx Cloud View detailed reasoning on Nx Cloud ↗


🎓 Learn more about Self-Healing CI on nx.dev

@anonrig

Copy link
Copy Markdown
ContributorAuthor

I'm closing this because Tanstack router repository has a limit of maximum number of pull-request open per a user and it is 6!!!!!

@anonriganonrig closed this Jun 22, 2026
@Sheraff

Copy link
Copy Markdown
Collaborator

I'm closing this because Tanstack router repository has a limit of maximum number of pull-request open per a user and it is 6!!!!!

I already had a pretty similar PR a while ago https://github.com/TanStack/router/pull/5071/changes

not sure why we never merged it.

Sign up for freeto 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.

2 participants

@anonrig@Sheraff
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

perf(router-core): guard default search parser against throws - #7665

Closed
anonrig wants to merge 1 commit into
TanStack:mainfrom
anonrig:perf/default-search-parse-guard
Closed

perf(router-core): guard default search parser against throws#7665
anonrig wants to merge 1 commit into
TanStack:mainfrom
anonrig:perf/default-search-parse-guard

Conversation

@anonrig

@anonriganonrig commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

What

Stop the default search parser from throwing a SyntaxError for every plain-string param. In packages/router-core/src/searchParams.ts, defaultParseSearch ran JSON.parse on every leftover string value:

-export const defaultParseSearch = parseSearchWith(JSON.parse)+export const defaultParseSearch = parseSearchWith(parseMaybeJson)

parseMaybeJson first checks, from the first non-whitespace character, whether the value could be JSON and only then calls JSON.parse. The check is a superset of JSON's value-start grammar ({ [ " -0-9t f n, skipping leading whitespace), so any string JSON.parse would accept still gets parsed — we only skip the doomed parse of plain strings.

Why

qss.decode already coerces numbers/booleans, so JSON.parse only ever runs on the remaining string values — which on real apps are mostly plain text (?q=hello&f=live&src=typed_query). Each one throws a SyntaxError that parseSearchWith catches to keep the raw string, and constructing + throwing that error (with stack capture) is the dominant cost.

Search params are parsed on every SSR request and every client navigation. In-situ before/after on the real defaultParseSearch (Vitest bench):

Query shapeold (JSON.parse)new (guarded)Speedup
mixed X-like (?q=…&f=live&src=…&pf=1)317K ops/s996K ops/s3.1×
all plain strings (?q=from:elon since:2024&f=live&…)107K ops/s176K ops/s1.7×

Correctness

Behavior is identical. The guard never skips a value JSON.parse would accept, and for skipped values it returns the raw string — exactly what parseSearchWith does today when JSON.parse throws.

Verified by:

  • The full existing searchParams suite (isomorphism round-trips + "alien deserialization" of human-typed params).
  • A 5000+ case fuzz confirming couldBeJson is a correct superset of JSON.parse's accept set.
  • Four added tests: plain-string params stay strings, leading-whitespace JSON values still parse (guard skips whitespace), JSON-looking-but-invalid values fall back to the raw string, and t/f/n-prefixed non-literals (tweet, false_alarm, null_island) stay strings.
nx run @tanstack/router-core:test:unit -> 1182 passed (+ 3 expected-fail)
nx run @tanstack/router-core:test:types -> no errors

Notes

  • Only the default parse path changes. defaultStringifySearch uses the same JSON.parse-as-throw-probe pattern when building URLs (also hot via <Link>), but its logic relies on throw-vs-no-throw as the "is this JSON?" signal, so making it allocation-free cleanly is a slightly larger change — happy to do it as a follow-up.
  • Includes a changeset (@tanstack/router-core patch).
  • Pre-existing unused-imports/no-unused-vars warnings on the existing catch (_err) blocks are untouched by this change.

Summary by CodeRabbit

  • Performance
    • Optimized query parameter parsing to reduce processing overhead, improving performance for server-side rendering and client-side navigation.

defaultParseSearch ran JSON.parse on every leftover string search value.
For plain strings (the common case, e.g. ?q=hello&f=live) JSON.parse
throws a SyntaxError that parseSearchWith catches to keep the raw string —
and constructing/throwing that error is the dominant cost.
Add a couldBeJson guard that decides from the first non-whitespace
character whether a value could be valid JSON. It is a superset of JSON's
value-start grammar, so any string JSON.parse would accept still gets
parsed (identical results); we only skip the guaranteed-throwing parse of
non-JSON strings.
Search params are parsed on every SSR request and every client
navigation. In-situ before/after on defaultParseSearch: ~3.1x faster on
mixed X-like queries, ~1.7x on an all-plain-string query.
@coderabbitai

coderabbitaiBot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 81d1c0ab-ecfc-4d71-a1b9-8028d3b188c0

📥 Commits

Reviewing files that changed from the base of the PR and between 279a849 and b4a3303.

📒 Files selected for processing (3)
  • .changeset/perf-default-search-parse-guard.md
  • packages/router-core/src/searchParams.ts
  • packages/router-core/tests/searchParams.test.ts

📝 Walkthrough

Walkthrough

Adds two internal helpers (couldBeJson and parseMaybeJson) to searchParams.ts. couldBeJson checks the first non-whitespace character to determine if a string could be a valid JSON value; parseMaybeJson calls JSON.parse only when that check passes. defaultParseSearch is updated to use parseMaybeJson instead of JSON.parse directly. New tests and a changeset accompany the change.

Changes

defaultParseSearch JSON parse guard

Layer / File(s)Summary
couldBeJson / parseMaybeJson helpers and defaultParseSearch wiring
packages/router-core/src/searchParams.ts, packages/router-core/tests/searchParams.test.ts, .changeset/perf-default-search-parse-guard.md
couldBeJson scans past leading whitespace and checks the first character against valid JSON-start characters. parseMaybeJson wraps JSON.parse behind that guard, returning the raw string when the check fails. defaultParseSearch is switched from parseSearchWith(JSON.parse) to parseSearchWith(parseMaybeJson). Tests cover plain strings, leading-whitespace JSON, invalid JSON-looking inputs, and non-literal t/f/n-prefixed words. The changeset documents the performance motivation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Suggested labels

package: router-core

Suggested reviewers

  • chorobin

Poem

🐇 Hop hop, no parse for thee,
A string that starts with h runs free!
No more throw/catch on every quest,
couldBeJson guards the nest.
Less overhead, more speedy ways—
The router hops through query haze! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'perf(router-core): guard default search parser against throws' directly and specifically describes the main change—adding a guard to prevent expensive JSON.parse throws in the default search parser, which is the core optimization in this PR.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

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

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

❤️ Share

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

@nx-cloud

nx-cloudBot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit b4a3303

CommandStatusDurationResult
nx affected --targets=test:eslint,test:unit,tes...❌ Failed22m 33sView ↗
nx run-many --target=build --exclude=examples/*...✅ Succeeded2m 17sView ↗

☁️ Nx Cloud last updated this comment at 2026-06-22 13:53:24 UTC

@pkg-pr-new

Copy link
Copy Markdown
More templates

@tanstack/arktype-adapter

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

@tanstack/eslint-plugin-router

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

@tanstack/eslint-plugin-start

npm i https://pkg.pr.new/@tanstack/eslint-plugin-start@7665

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-rsc

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-fn-stubs

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

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

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: b4a3303

@codspeed-hq

Copy link
Copy Markdown

Merging this PR will degrade performance by 11.83%

❌ 3 regressed benchmarks
✅ 141 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

ModeBenchmarkBASEHEADEfficiency
Memorymem serialization-payload (vue)6.8 MB8.7 MB-22.2%
Memorymem aborted-requests (vue)959.1 KB1,048.9 KB-8.56%
Memorymem request-churn (vue)1.3 MB1.3 MB-3.66%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing anonrig:perf/default-search-parse-guard (b4a3303) with main (f23ed0f)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (279a849) during the generation of this report, so f23ed0f was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@nx-cloudnx-cloudBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nx Cloud has identified a possible root cause for your failed CI:

We classified this failure as an environment state issue rather than a code change. The error — a missing resolveRuntimeSuffix export in e2e/e2e-utils/dist/esm/index.js — points to stale or inconsistently built dist artifacts in the e2e test infrastructure, which has no relation to the @tanstack/router-core search param optimization introduced by this PR. Rebuilding the e2e-utils package should restore consistent artifacts and resolve the failure.

No code changes were suggested for this issue.

You can trigger a rerun by pushing an empty commit:

git commit --allow-empty -m "chore: trigger rerun"
git push

Nx Cloud View detailed reasoning on Nx Cloud ↗


🎓 Learn more about Self-Healing CI on nx.dev

@anonrig

Copy link
Copy Markdown
ContributorAuthor

I'm closing this because Tanstack router repository has a limit of maximum number of pull-request open per a user and it is 6!!!!!

@anonriganonrig closed this Jun 22, 2026
@Sheraff

Copy link
Copy Markdown
Collaborator

I'm closing this because Tanstack router repository has a limit of maximum number of pull-request open per a user and it is 6!!!!!

I already had a pretty similar PR a while ago https://github.com/TanStack/router/pull/5071/changes

not sure why we never merged it.

Sign up for freeto 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.

2 participants

@anonrig@Sheraff
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

perf(router-core): guard default search parser against throws - #7665

Closed
anonrig wants to merge 1 commit into
TanStack:mainfrom
anonrig:perf/default-search-parse-guard
Closed

perf(router-core): guard default search parser against throws#7665
anonrig wants to merge 1 commit into
TanStack:mainfrom
anonrig:perf/default-search-parse-guard

Conversation

@anonrig

@anonriganonrig commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

What

Stop the default search parser from throwing a SyntaxError for every plain-string param. In packages/router-core/src/searchParams.ts, defaultParseSearch ran JSON.parse on every leftover string value:

-export const defaultParseSearch = parseSearchWith(JSON.parse)+export const defaultParseSearch = parseSearchWith(parseMaybeJson)

parseMaybeJson first checks, from the first non-whitespace character, whether the value could be JSON and only then calls JSON.parse. The check is a superset of JSON's value-start grammar ({ [ " -0-9t f n, skipping leading whitespace), so any string JSON.parse would accept still gets parsed — we only skip the doomed parse of plain strings.

Why

qss.decode already coerces numbers/booleans, so JSON.parse only ever runs on the remaining string values — which on real apps are mostly plain text (?q=hello&f=live&src=typed_query). Each one throws a SyntaxError that parseSearchWith catches to keep the raw string, and constructing + throwing that error (with stack capture) is the dominant cost.

Search params are parsed on every SSR request and every client navigation. In-situ before/after on the real defaultParseSearch (Vitest bench):

Query shapeold (JSON.parse)new (guarded)Speedup
mixed X-like (?q=…&f=live&src=…&pf=1)317K ops/s996K ops/s3.1×
all plain strings (?q=from:elon since:2024&f=live&…)107K ops/s176K ops/s1.7×

Correctness

Behavior is identical. The guard never skips a value JSON.parse would accept, and for skipped values it returns the raw string — exactly what parseSearchWith does today when JSON.parse throws.

Verified by:

  • The full existing searchParams suite (isomorphism round-trips + "alien deserialization" of human-typed params).
  • A 5000+ case fuzz confirming couldBeJson is a correct superset of JSON.parse's accept set.
  • Four added tests: plain-string params stay strings, leading-whitespace JSON values still parse (guard skips whitespace), JSON-looking-but-invalid values fall back to the raw string, and t/f/n-prefixed non-literals (tweet, false_alarm, null_island) stay strings.
nx run @tanstack/router-core:test:unit -> 1182 passed (+ 3 expected-fail)
nx run @tanstack/router-core:test:types -> no errors

Notes

  • Only the default parse path changes. defaultStringifySearch uses the same JSON.parse-as-throw-probe pattern when building URLs (also hot via <Link>), but its logic relies on throw-vs-no-throw as the "is this JSON?" signal, so making it allocation-free cleanly is a slightly larger change — happy to do it as a follow-up.
  • Includes a changeset (@tanstack/router-core patch).
  • Pre-existing unused-imports/no-unused-vars warnings on the existing catch (_err) blocks are untouched by this change.

Summary by CodeRabbit

  • Performance
    • Optimized query parameter parsing to reduce processing overhead, improving performance for server-side rendering and client-side navigation.

defaultParseSearch ran JSON.parse on every leftover string search value.
For plain strings (the common case, e.g. ?q=hello&f=live) JSON.parse
throws a SyntaxError that parseSearchWith catches to keep the raw string —
and constructing/throwing that error is the dominant cost.
Add a couldBeJson guard that decides from the first non-whitespace
character whether a value could be valid JSON. It is a superset of JSON's
value-start grammar, so any string JSON.parse would accept still gets
parsed (identical results); we only skip the guaranteed-throwing parse of
non-JSON strings.
Search params are parsed on every SSR request and every client
navigation. In-situ before/after on defaultParseSearch: ~3.1x faster on
mixed X-like queries, ~1.7x on an all-plain-string query.
@coderabbitai

coderabbitaiBot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 81d1c0ab-ecfc-4d71-a1b9-8028d3b188c0

📥 Commits

Reviewing files that changed from the base of the PR and between 279a849 and b4a3303.

📒 Files selected for processing (3)
  • .changeset/perf-default-search-parse-guard.md
  • packages/router-core/src/searchParams.ts
  • packages/router-core/tests/searchParams.test.ts

📝 Walkthrough

Walkthrough

Adds two internal helpers (couldBeJson and parseMaybeJson) to searchParams.ts. couldBeJson checks the first non-whitespace character to determine if a string could be a valid JSON value; parseMaybeJson calls JSON.parse only when that check passes. defaultParseSearch is updated to use parseMaybeJson instead of JSON.parse directly. New tests and a changeset accompany the change.

Changes

defaultParseSearch JSON parse guard

Layer / File(s)Summary
couldBeJson / parseMaybeJson helpers and defaultParseSearch wiring
packages/router-core/src/searchParams.ts, packages/router-core/tests/searchParams.test.ts, .changeset/perf-default-search-parse-guard.md
couldBeJson scans past leading whitespace and checks the first character against valid JSON-start characters. parseMaybeJson wraps JSON.parse behind that guard, returning the raw string when the check fails. defaultParseSearch is switched from parseSearchWith(JSON.parse) to parseSearchWith(parseMaybeJson). Tests cover plain strings, leading-whitespace JSON, invalid JSON-looking inputs, and non-literal t/f/n-prefixed words. The changeset documents the performance motivation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Suggested labels

package: router-core

Suggested reviewers

  • chorobin

Poem

🐇 Hop hop, no parse for thee,
A string that starts with h runs free!
No more throw/catch on every quest,
couldBeJson guards the nest.
Less overhead, more speedy ways—
The router hops through query haze! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'perf(router-core): guard default search parser against throws' directly and specifically describes the main change—adding a guard to prevent expensive JSON.parse throws in the default search parser, which is the core optimization in this PR.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

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

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

❤️ Share

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

@nx-cloud

nx-cloudBot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit b4a3303

CommandStatusDurationResult
nx affected --targets=test:eslint,test:unit,tes...❌ Failed22m 33sView ↗
nx run-many --target=build --exclude=examples/*...✅ Succeeded2m 17sView ↗

☁️ Nx Cloud last updated this comment at 2026-06-22 13:53:24 UTC

@pkg-pr-new

Copy link
Copy Markdown
More templates

@tanstack/arktype-adapter

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

@tanstack/eslint-plugin-router

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

@tanstack/eslint-plugin-start

npm i https://pkg.pr.new/@tanstack/eslint-plugin-start@7665

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-rsc

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-fn-stubs

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

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

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: b4a3303

@codspeed-hq

Copy link
Copy Markdown

Merging this PR will degrade performance by 11.83%

❌ 3 regressed benchmarks
✅ 141 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

ModeBenchmarkBASEHEADEfficiency
Memorymem serialization-payload (vue)6.8 MB8.7 MB-22.2%
Memorymem aborted-requests (vue)959.1 KB1,048.9 KB-8.56%
Memorymem request-churn (vue)1.3 MB1.3 MB-3.66%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing anonrig:perf/default-search-parse-guard (b4a3303) with main (f23ed0f)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (279a849) during the generation of this report, so f23ed0f was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@nx-cloudnx-cloudBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nx Cloud has identified a possible root cause for your failed CI:

We classified this failure as an environment state issue rather than a code change. The error — a missing resolveRuntimeSuffix export in e2e/e2e-utils/dist/esm/index.js — points to stale or inconsistently built dist artifacts in the e2e test infrastructure, which has no relation to the @tanstack/router-core search param optimization introduced by this PR. Rebuilding the e2e-utils package should restore consistent artifacts and resolve the failure.

No code changes were suggested for this issue.

You can trigger a rerun by pushing an empty commit:

git commit --allow-empty -m "chore: trigger rerun"
git push

Nx Cloud View detailed reasoning on Nx Cloud ↗


🎓 Learn more about Self-Healing CI on nx.dev

@anonrig

Copy link
Copy Markdown
ContributorAuthor

I'm closing this because Tanstack router repository has a limit of maximum number of pull-request open per a user and it is 6!!!!!

@anonriganonrig closed this Jun 22, 2026
@Sheraff

Copy link
Copy Markdown
Collaborator

I'm closing this because Tanstack router repository has a limit of maximum number of pull-request open per a user and it is 6!!!!!

I already had a pretty similar PR a while ago https://github.com/TanStack/router/pull/5071/changes

not sure why we never merged it.

Sign up for freeto 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.

2 participants

@anonrig@Sheraff
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

perf(router-core): guard default search parser against throws - #7665

Closed
anonrig wants to merge 1 commit into
TanStack:mainfrom
anonrig:perf/default-search-parse-guard
Closed

perf(router-core): guard default search parser against throws#7665
anonrig wants to merge 1 commit into
TanStack:mainfrom
anonrig:perf/default-search-parse-guard

Conversation

@anonrig

@anonriganonrig commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

What

Stop the default search parser from throwing a SyntaxError for every plain-string param. In packages/router-core/src/searchParams.ts, defaultParseSearch ran JSON.parse on every leftover string value:

-export const defaultParseSearch = parseSearchWith(JSON.parse)+export const defaultParseSearch = parseSearchWith(parseMaybeJson)

parseMaybeJson first checks, from the first non-whitespace character, whether the value could be JSON and only then calls JSON.parse. The check is a superset of JSON's value-start grammar ({ [ " -0-9t f n, skipping leading whitespace), so any string JSON.parse would accept still gets parsed — we only skip the doomed parse of plain strings.

Why

qss.decode already coerces numbers/booleans, so JSON.parse only ever runs on the remaining string values — which on real apps are mostly plain text (?q=hello&f=live&src=typed_query). Each one throws a SyntaxError that parseSearchWith catches to keep the raw string, and constructing + throwing that error (with stack capture) is the dominant cost.

Search params are parsed on every SSR request and every client navigation. In-situ before/after on the real defaultParseSearch (Vitest bench):

Query shapeold (JSON.parse)new (guarded)Speedup
mixed X-like (?q=…&f=live&src=…&pf=1)317K ops/s996K ops/s3.1×
all plain strings (?q=from:elon since:2024&f=live&…)107K ops/s176K ops/s1.7×

Correctness

Behavior is identical. The guard never skips a value JSON.parse would accept, and for skipped values it returns the raw string — exactly what parseSearchWith does today when JSON.parse throws.

Verified by:

  • The full existing searchParams suite (isomorphism round-trips + "alien deserialization" of human-typed params).
  • A 5000+ case fuzz confirming couldBeJson is a correct superset of JSON.parse's accept set.
  • Four added tests: plain-string params stay strings, leading-whitespace JSON values still parse (guard skips whitespace), JSON-looking-but-invalid values fall back to the raw string, and t/f/n-prefixed non-literals (tweet, false_alarm, null_island) stay strings.
nx run @tanstack/router-core:test:unit -> 1182 passed (+ 3 expected-fail)
nx run @tanstack/router-core:test:types -> no errors

Notes

  • Only the default parse path changes. defaultStringifySearch uses the same JSON.parse-as-throw-probe pattern when building URLs (also hot via <Link>), but its logic relies on throw-vs-no-throw as the "is this JSON?" signal, so making it allocation-free cleanly is a slightly larger change — happy to do it as a follow-up.
  • Includes a changeset (@tanstack/router-core patch).
  • Pre-existing unused-imports/no-unused-vars warnings on the existing catch (_err) blocks are untouched by this change.

Summary by CodeRabbit

  • Performance
    • Optimized query parameter parsing to reduce processing overhead, improving performance for server-side rendering and client-side navigation.

defaultParseSearch ran JSON.parse on every leftover string search value.
For plain strings (the common case, e.g. ?q=hello&f=live) JSON.parse
throws a SyntaxError that parseSearchWith catches to keep the raw string —
and constructing/throwing that error is the dominant cost.
Add a couldBeJson guard that decides from the first non-whitespace
character whether a value could be valid JSON. It is a superset of JSON's
value-start grammar, so any string JSON.parse would accept still gets
parsed (identical results); we only skip the guaranteed-throwing parse of
non-JSON strings.
Search params are parsed on every SSR request and every client
navigation. In-situ before/after on defaultParseSearch: ~3.1x faster on
mixed X-like queries, ~1.7x on an all-plain-string query.
@coderabbitai

coderabbitaiBot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 81d1c0ab-ecfc-4d71-a1b9-8028d3b188c0

📥 Commits

Reviewing files that changed from the base of the PR and between 279a849 and b4a3303.

📒 Files selected for processing (3)
  • .changeset/perf-default-search-parse-guard.md
  • packages/router-core/src/searchParams.ts
  • packages/router-core/tests/searchParams.test.ts

📝 Walkthrough

Walkthrough

Adds two internal helpers (couldBeJson and parseMaybeJson) to searchParams.ts. couldBeJson checks the first non-whitespace character to determine if a string could be a valid JSON value; parseMaybeJson calls JSON.parse only when that check passes. defaultParseSearch is updated to use parseMaybeJson instead of JSON.parse directly. New tests and a changeset accompany the change.

Changes

defaultParseSearch JSON parse guard

Layer / File(s)Summary
couldBeJson / parseMaybeJson helpers and defaultParseSearch wiring
packages/router-core/src/searchParams.ts, packages/router-core/tests/searchParams.test.ts, .changeset/perf-default-search-parse-guard.md
couldBeJson scans past leading whitespace and checks the first character against valid JSON-start characters. parseMaybeJson wraps JSON.parse behind that guard, returning the raw string when the check fails. defaultParseSearch is switched from parseSearchWith(JSON.parse) to parseSearchWith(parseMaybeJson). Tests cover plain strings, leading-whitespace JSON, invalid JSON-looking inputs, and non-literal t/f/n-prefixed words. The changeset documents the performance motivation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Suggested labels

package: router-core

Suggested reviewers

  • chorobin

Poem

🐇 Hop hop, no parse for thee,
A string that starts with h runs free!
No more throw/catch on every quest,
couldBeJson guards the nest.
Less overhead, more speedy ways—
The router hops through query haze! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'perf(router-core): guard default search parser against throws' directly and specifically describes the main change—adding a guard to prevent expensive JSON.parse throws in the default search parser, which is the core optimization in this PR.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

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

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

❤️ Share

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

@nx-cloud

nx-cloudBot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit b4a3303

CommandStatusDurationResult
nx affected --targets=test:eslint,test:unit,tes...❌ Failed22m 33sView ↗
nx run-many --target=build --exclude=examples/*...✅ Succeeded2m 17sView ↗

☁️ Nx Cloud last updated this comment at 2026-06-22 13:53:24 UTC

@pkg-pr-new

Copy link
Copy Markdown
More templates

@tanstack/arktype-adapter

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

@tanstack/eslint-plugin-router

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

@tanstack/eslint-plugin-start

npm i https://pkg.pr.new/@tanstack/eslint-plugin-start@7665

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-rsc

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-fn-stubs

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

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

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: b4a3303

@codspeed-hq

Copy link
Copy Markdown

Merging this PR will degrade performance by 11.83%

❌ 3 regressed benchmarks
✅ 141 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

ModeBenchmarkBASEHEADEfficiency
Memorymem serialization-payload (vue)6.8 MB8.7 MB-22.2%
Memorymem aborted-requests (vue)959.1 KB1,048.9 KB-8.56%
Memorymem request-churn (vue)1.3 MB1.3 MB-3.66%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing anonrig:perf/default-search-parse-guard (b4a3303) with main (f23ed0f)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (279a849) during the generation of this report, so f23ed0f was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@nx-cloudnx-cloudBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nx Cloud has identified a possible root cause for your failed CI:

We classified this failure as an environment state issue rather than a code change. The error — a missing resolveRuntimeSuffix export in e2e/e2e-utils/dist/esm/index.js — points to stale or inconsistently built dist artifacts in the e2e test infrastructure, which has no relation to the @tanstack/router-core search param optimization introduced by this PR. Rebuilding the e2e-utils package should restore consistent artifacts and resolve the failure.

No code changes were suggested for this issue.

You can trigger a rerun by pushing an empty commit:

git commit --allow-empty -m "chore: trigger rerun"
git push

Nx Cloud View detailed reasoning on Nx Cloud ↗


🎓 Learn more about Self-Healing CI on nx.dev

@anonrig

Copy link
Copy Markdown
ContributorAuthor

I'm closing this because Tanstack router repository has a limit of maximum number of pull-request open per a user and it is 6!!!!!

@anonriganonrig closed this Jun 22, 2026
@Sheraff

Copy link
Copy Markdown
Collaborator

I'm closing this because Tanstack router repository has a limit of maximum number of pull-request open per a user and it is 6!!!!!

I already had a pretty similar PR a while ago https://github.com/TanStack/router/pull/5071/changes

not sure why we never merged it.

Sign up for freeto 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.

2 participants

@anonrig@Sheraff