T
TanStack•16mo ago
stormy-gold

How do I stop search params from being automatically converted to number?

In validateSearch, a search param that's alphanumeric is coming in as a number if there are no letters in the value. Is this expected? My input validation with Valibot rejects it because of that. I really wouldn't expect the router to do anything with the value before validateSearch runs. For example, using URL the value is parsed as a string, not number.
window.location.href // 'http://localhost:3000?code=123'
new URL(window.location.href).searchParams.get('code') // '123' (string)

export const Route = createFileRoute('/')({
validateSearch(search) {
search.code // 123 (number)
}
})
window.location.href // 'http://localhost:3000?code=123'
new URL(window.location.href).searchParams.get('code') // '123' (string)

export const Route = createFileRoute('/')({
validateSearch(search) {
search.code // 123 (number)
}
})
17 Replies
molecular-blue
molecular-blue•16mo ago
can you show a reproduction by forking one of our examples please? I remember having to use z.coerce explicitly because I'm getting in strings for numbers
stormy-gold
stormy-goldOP•16mo ago
Yeah of course, just wanted to check whether this is expected behavior or not first Here's a repro, although using useSearch which gives the same result https://stackblitz.com/edit/github-8tcva3?file=src%2Froutes%2Findex.tsx
molecular-blue
molecular-blue•16mo ago
thanks, you're right. Even the input to validateSearchalready has code as number @Manuel Schiller is taht on purpose? Tbh I don't think so. Also, for validateSearch, the input is typed as {}. In the docs, we manually put a type there as Record<string, unknown>. Why isn't that the default?
molecular-blue
molecular-blue•16mo ago
aah okay I got it. It seems like our defaultSearch parsing uses qss and that converts strings to numbers, and booleans as well https://github.com/TanStack/router/blob/ee5bb089f2336dc0f411149360f996d04b615ef9/packages/react-router/src/qss.ts#L47-L53
GitHub
router/packages/react-router/src/qss.ts at ee5bb089f2336dc0f4111493...
🤖 Fully typesafe Router for React (and friends) w/ built-in caching, 1st class search-param APIs, client-side cache integration and isomorphic rendering. - TanStack/router
molecular-blue
molecular-blue•16mo ago
it can be customized: https://tanstack.com/router/latest/docs/framework/react/guide/custom-search-param-serialization#custom-search-param-serialization I think docs aren't fully up to date because it mentions:
By default, TanStack Router parses and serializes your search params automatically using JSON.stringify/JSON.parse
but it does more parsing additionally
Custom Search Param Serialization | TanStack Router React Docs
TODO This portion of documentation is currently under construction By default, TanStack Router parses and serializes your search params automatically using JSON.stringify/JSON.parse. Depending on your needs though, you may want to customize the serialization process.
molecular-blue
molecular-blue•16mo ago
I still think that default type should be Record<string, unknown>: https://github.com/TanStack/router/blob/6c1d831b2747222d5cbbbe4b61df3308bd05a09b/packages/react-router/src/route.ts#L114 thoughts @Chris Horobin ?
GitHub
router/packages/react-router/src/route.ts at 6c1d831b2747222d5cbbbe...
🤖 Fully typesafe Router for React (and friends) w/ built-in caching, 1st class search-param APIs, client-side cache integration and isomorphic rendering. - TanStack/router
stormy-gold
stormy-goldOP•16mo ago
Yeah I agree on the type. Would be much easier to access properties on it if it was Record<string, unknown>
sunny-green
sunny-green•16mo ago
I agree. {} just means anything that's not null or undefined which is not correct likely
stormy-gold
stormy-goldOP•16mo ago
So for the parsing, are the docs wrong or the code? 😅
stormy-gold
stormy-goldOP•16mo ago
When doing this
parseSearch: parseSearchWith(JSON.parse),
stringifySearch: stringifySearchWith(JSON.stringify),
parseSearch: parseSearchWith(JSON.parse),
stringifySearch: stringifySearchWith(JSON.stringify),
I'm getting a really weird result. The search param switches between number and string a couple of times on load.
No description
stormy-gold
stormy-goldOP•16mo ago
I'll see if I can reproduce this in the stackblitz
stormy-gold
stormy-goldOP•16mo ago
Yeah parseSearch/stringifySearch does not seem to help here. It's still number https://stackblitz.com/edit/github-8tcva3-d7fvbp?file=src%2Fmain.tsx
Jakob Norlin
StackBlitz
Router Quickstart File Based Example (forked) - StackBlitz
Run official live example code for Router Quickstart File Based, created by Tanstack on StackBlitz
molecular-blue
molecular-blue•16mo ago
GitHub
router/packages/react-router/src/searchParams.ts at 41391cdd4a41ea5...
🤖 Fully typesafe Router for React (and friends) w/ built-in caching, 1st class search-param APIs, client-side cache integration and isomorphic rendering. - TanStack/router
molecular-blue
molecular-blue•16mo ago
and then the parser is run on every element so it seems like you'll get boolean for true/false, number for number and string for everything else could be a feature 😂 pathParams are different though - they are always a string. Not sure if that difference is a good thing :/
stormy-gold
stormy-goldOP•16mo ago
No honestly I would expect to always get strings back as a default Luckily I tested with a numeric string to start off, but otherwise I would have probably pushed to prod a broken implementation, since we do schema validation in validateSearch which would fail on some values
adverse-sapphire
adverse-sapphire•16mo ago
@Tanner Linsley do we really want to "pre-parse" search params into "best fitting" types prior to validateSearch? Or should we default to strings?
jolly-crimson
jolly-crimson•16mo ago
Not everyone will put the effort into validation. This is still better than the default experience imo. We have to do some level of pre parsing to go from Record of string,string to JSON Validation relies on json. And there is a difference between storing a string/number/bool in the URL. That difference has just never been formalized before.

Did you find this page helpful?