Router searchParameter gets reset to default value (of zod schema) after navigations (and using retainSearchParams)
Which project does this relate to?
Router
Describe the bug
Using retained searchParams, if we change the retained param x form a to b on some route (lets say route-a) and we navigate to route-b, which doesn't change param x, param x will revert back to its default value.
This is a regression because it works on using older versions.
Same code older versions: stackblitz Same code latest versions: stackblitz
Your Example Website or App
-see desc
Steps to Reproduce the Bug or Issue
- click on Home route (see Auth defaults to true)
- click on Settings route (see Auth remains true)
- click on About route (see now Auth is false)
- click on Home route: 4.a older version: Auth remains as false (which is expected) 4.b newer version: Auth changes back to true (which is unexpected)
Expected behavior
after chaning the retained searchParams, it should stay unchanged after navigations, and only change if explicitly said so.
- some other weird thing here is that even without using this middle params stay on URL, kinda making retainSearchParams useless, unless im not grasping the use of it correctly.
Screenshots or Videos
No response
Platform
- OS: [e.g. macOS, Windows, Linux]
- Browser: [e.g. Chrome, Safari, Firefox]
- Version: [e.g. 91.1]
Additional context
No response
which version exactly introduced the regression?
hi @schiller-manuel, its specifically PR #2747, works on @tanstack/react-router 1.81.4, doesnt on 1.81.5
I'm seeing the same issue too
same here, can confirm that 1.81.5 introduced this bug
@schiller-manuel can we fix this please, its really bugging me :(
Seeing the same issue. Confirmed on my end this broke going from 1.81.4 to 1.81.5
@schiller-manuel this bug destroys all navigations with default searchParams (overrides provided params with defaults). Can we make this a priority please? >_<
@schiller-manuel I will second @hakoomen. I got bit by this bug for a while. this would be great to get fixed, it does break navigation for me when going between pages.
@schiller-manuel this bug destroys all navigations with default searchParams (overrides provided params with defaults). Can we make this a priority please? >_<
for all link components or navigate calls we have to pass the current sp values like:
await router.navigate({
to: "/",
search: (prev) => prev
});
which is another paintpoint
Can confirm the bug; The problem is that our internal validate middleware (that comes from validateSearchParams) runs first, so it sees an empty search and applies the schema, which adds default values. That happens before retainSearchParams runs).
It’s not as easy as just flipping the order though, as somethings have to run after validate. A bit tricky.
The problem is that our internal
validatemiddleware (that comes fromvalidateSearchParams) runs first
Total newbie idea: Is it possible to expose the validateSearchParams middleware so that users have the option to control when validation happens? 🤔
Users could then work around this issue by placing the validation after the retainSearchParams.
e.g.
validateSearch: undefined,
search: {
middlewares: [
retainSearchParams(["foo"]),
validateSearchParams(routeSchema),
],
},
It also gives users greater control for their more complex use cases.
I dunno if it is related, and it might have to do with StrictMode, but when I open a url with search params, the first time I have access to them, the second time, they are undefined.
Is it also because of the validate?
+1
Anyone has a solution or workaround for this issue?
I tried to make my search params optional() instead of providing default() values. Beside the fact that now my application has to deal with the optional/undefined values (not a big deal here), activeProps from Link does not work really well.
In this example:
// I'd like to have 'id' as the default for 'orderBy':
const RouteSearchParams = z.object({
orderBy: z.enum([ "id", "name" ]).default("id"),
});
// but with retainSearchParams I have to:
const RouteSearchParams = z.object({
orderBy: z.enum([ "id", "name" ]).optional(),
});
When using a Link like this (no orderBy, my app code would handle undefined)
<Link to="..." search={{orderBy: undefined}}>...</Link>
the Link is always active, even when the current search is set to name.
On the other hand when using a Link like this:
<Link to="..." search={{orderBy: "id" }}>...</Link>
the link is (of course) not active when there is no orderBy search at all currently.
I tried with and without zod-adapter and tried zod v3 and v4.
I also tried to handle that somehow in an own middleware but unfortunately failed.
since this is still unfixed i added a workaround in our app to be able to upgrade. if anyone is interested in it:
- convert all
navigate()calls and<Link>sto pass the previous search into the new search likenavigate({..., search: prev => prev}) - add a search middleware that strips out invalid search params and place it everywhere you've used
retainSearchParamsprior. sample:
return ({ search, next }) => {
const result = next(search);
const filteredResult = {};
Object.keys(result).forEach(key => {
if (validKeys.includes(key)) {
filteredResult[key] =
result[key];
}
});
return filteredResult;
};
where validKeys are the keys of your search schema shape (e.g. Object.keys(yourSearchSchema.shape)
as a side note: make sure your schemas use the fallback pattern mentioned in the docs https://tanstack.com/router/latest/docs/framework/react/guide/search-params#zod