T
TanStack7mo ago
variable-lime

Route validators (params/search) when using TanStack Start

Hey guys, recently started using TanStack Start, amazing so far but encountered this quirk, I'm having a bit of trouble with route validators for params and search. When navigating within the app everything works as expected (same as using router on client apps only) but when paired with start i encountered an issue when entering the page from outside or just refreshing then the validators wont work, here is a short code to demonstrate.
// posts.$id.tsx
export const Route = createFileRoute('/posts/$id')({
params: z.object({
id: z.coerce.number().int().positive().min(1).max(10),
}),
validateSearch: z.object({
sort: z
.enum(['asc', 'desc'], { message: 'Sort must be either asc or desc' })
.default('asc'),
}),
errorComponent: ({ error }) => <div>route error: {error.message}</div>,
component: RouteComponent,
})

function RouteComponent() {
const { id } = Route.useParams()
const { sort } = Route.useSearch()

return (
<div>
Hello Post ID: {id} / SORT: {sort}
<Link to="/posts">Go Back</Link>
</div>
)
}
// posts.$id.tsx
export const Route = createFileRoute('/posts/$id')({
params: z.object({
id: z.coerce.number().int().positive().min(1).max(10),
}),
validateSearch: z.object({
sort: z
.enum(['asc', 'desc'], { message: 'Sort must be either asc or desc' })
.default('asc'),
}),
errorComponent: ({ error }) => <div>route error: {error.message}</div>,
component: RouteComponent,
})

function RouteComponent() {
const { id } = Route.useParams()
const { sort } = Route.useSearch()

return (
<div>
Hello Post ID: {id} / SORT: {sort}
<Link to="/posts">Go Back</Link>
</div>
)
}
I navigate to the route within the app via Link component or navigate etc, everything works fine.
<Link to="/posts/$id" params={{ id: 5 }} search={{ sort: 'asc' }}>
<Link to="/posts/$id" params={{ id: 5 }} search={{ sort: 'asc' }}>
Page loads as expected - works same for client router only and for start, when I navigate to not match the validator via Link/navigate.
<Link to="/posts/$id" params={{ id: 5 }} search={{ sort: 'abc' }}>
<Link to="/posts/$id" params={{ id: 5 }} search={{ sort: 'abc' }}>
Again page loads and throws validator error on both start and if runing rounter on client only. The actual issue happens when using tanstack staart and refreshing the page or entring the page with a url from outside the app for the route with wrong params/search. The validators wont trigger and the page will be shown normally (with wrong param/search). It's probably got to do something with SSR. Sometimes the error will flash for a 100ms or so and go back to display the page without validating params/search. Anyone know of a solution for this? I tried some workarounds with redirecting in onError in route, or messing around with using notFound() in validators but it wasnt consistent and triggered some other issues. Thanks.
7 Replies
adverse-sapphire
adverse-sapphire7mo ago
can you please provide a complete minimal example ? as a GitHub repo
variable-lime
variable-limeOP7mo ago
https://github.com/Rothiusx/tanstack-start-route-validators This is the minimal repo for this issue, created with create-tsrouter-app and using TanStack Start and everything kept default, only stripped example routes
GitHub
GitHub - Rothiusx/tanstack-start-route-validators
Contribute to Rothiusx/tanstack-start-route-validators development by creating an account on GitHub.
adverse-sapphire
adverse-sapphire7mo ago
yeah there is a hydration mismatch seems to be a bug in start could you please create a github issue for this and add that reproducer?
variable-lime
variable-limeOP7mo ago
yeah sure, created the issue
harsh-harlequin
harsh-harlequin4mo ago
@Rothius I am having the same issue. Everything renders (including loader functions). Did you find any workaround to this?
variable-lime
variable-limeOP4mo ago
if you really just need to to block the render of wrong params/search params you can add onError callback with redirect to the route like this
params: ...
validateSearch: ...
onError: (error) => {
throw redirect({ to: '/someroute' })
},
component: ...
params: ...
validateSearch: ...
onError: (error) => {
throw redirect({ to: '/someroute' })
},
component: ...
this will stop the route from going through the validators on hard refresh/reload, you can use error.message to get the zod error message or any other validation error you use
harsh-harlequin
harsh-harlequin4mo ago
I was not familiar with onError! That is an excellent workaround for now. Thank you so much!

Did you find this page helpful?