T
TanStack9mo ago
stormy-gold

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
fascinating-indigo
fascinating-indigo9mo ago
can you please provide a complete minimal example ? as a GitHub repo
stormy-gold
stormy-goldOP9mo 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.
fascinating-indigo
fascinating-indigo9mo 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?
stormy-gold
stormy-goldOP9mo ago
yeah sure, created the issue
optimistic-gold
optimistic-gold6mo ago
@Rothius I am having the same issue. Everything renders (including loader functions). Did you find any workaround to this?
stormy-gold
stormy-goldOP6mo 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
optimistic-gold
optimistic-gold6mo ago
I was not familiar with onError! That is an excellent workaround for now. Thank you so much!

Did you find this page helpful?