T
TanStack4w ago
harsh-harlequin

Recommendations for Zod validated Nested Search Params

I've reviewed the docs on Search Params + Zod several times and haven't really found a nice way to deal with typing a nested object in my URL state. I made this minimal example (see the /about route) that shows off some of the tricky pieces I can't seem to work around: 1. any params that use the fallback() function from @tanstack/zod-adapter force their key into the URL (which is just preference, but clutters up the URL bar) Should I just accept that the URL is going to be a little ugly? Or do I have to give up the type safety to achieve it? 2. nested objects that use fallback() have to wrap every single key in the generic function, leading to defining the default values N times Are deeply nested search params considered a good practice? Or is there some way to avoid needing to duplicate default values over and over? To demonstrate the nesting issue:
// this:
nested_object: z
.object({
slightly_nested: z.boolean().catch(false),
more_nesting: z.object({
deeply_nested: z.string().catch(''),
}),
})
.optional(),

// turns into this with fallback:
fallback_nested_object: fallback(
z
.object({
slightly_nested: fallback(z.boolean(), false).default(false),
more_nesting: fallback(
z.object({
deeply_nested: fallback(z.string(), '').default(''),
}),
{
deeply_nested: '',
}
).default({
deeply_nested: '',
}),
})
.optional(),
{
slightly_nested: false,
more_nesting: {
deeply_nested: '',
},
}
).default({
slightly_nested: false,
more_nesting: {
deeply_nested: '',
},
}),
// this:
nested_object: z
.object({
slightly_nested: z.boolean().catch(false),
more_nesting: z.object({
deeply_nested: z.string().catch(''),
}),
})
.optional(),

// turns into this with fallback:
fallback_nested_object: fallback(
z
.object({
slightly_nested: fallback(z.boolean(), false).default(false),
more_nesting: fallback(
z.object({
deeply_nested: fallback(z.string(), '').default(''),
}),
{
deeply_nested: '',
}
).default({
deeply_nested: '',
}),
})
.optional(),
{
slightly_nested: false,
more_nesting: {
deeply_nested: '',
},
}
).default({
slightly_nested: false,
more_nesting: {
deeply_nested: '',
},
}),
Curious if there is a better way! SOLVED: upgrading to Zod v4 removes the need to use the fallback generic function.
Kyle Gill
StackBlitz
TanStack Router + Zod (Deep nesting params with fallback) - StackBlitz
Run official live example code for Router Quickstart File Based, created by Tanstack on StackBlitz
12 Replies
afraid-scarlet
afraid-scarlet4w ago
zod4 does not need fallback anymore maybe try that out
harsh-harlequin
harsh-harlequinOP4w ago
Holy toledo!! Will do and will report back thanks for the quick reply
afraid-scarlet
afraid-scarlet4w ago
we just added fallback to make that typesafe we will soon deprecate the adapter as it's not necessary for zod4 anymore
harsh-harlequin
harsh-harlequinOP4w ago
well I'll be darned!! That's working!
No description
harsh-harlequin
harsh-harlequinOP4w ago
Oh that screenshot didn't show the types like I thought it would but the upgrade is working without fallback
afraid-scarlet
afraid-scarlet4w ago
nice
harsh-harlequin
harsh-harlequinOP4w ago
Thanks @Manuel Schiller ! Is that worth me making a PR to the docs to make note of?
afraid-scarlet
afraid-scarlet4w ago
we have one I think! but did not merge yet please have a look at the open PRs
harsh-harlequin
harsh-harlequinOP4w ago
Will do
afraid-scarlet
afraid-scarlet4w ago
we should also deprecate the zod adapter .... and maybe really recommend people upgrading to zod4 in the docs
harsh-harlequin
harsh-harlequinOP4w ago
GitHub
docs: add info about zod 4 by niba · Pull Request #4823 · TanStac...
Add a note about zod 4. I think that at some point we can rewrite the entire doc about search-params because adapter becomes a special case only for zod 3 related: #4442
harsh-harlequin
harsh-harlequinOP4w ago
Found it, even better Thanks again

Did you find this page helpful?