Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/router/api/router/RouteOptionsType.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ The `RouteOptions` type accepts an object with the following properties:

### `validateSearch` method

- Type: `(rawSearchParams: unknown) => TSearchSchema`
- Type: `(searchParams: unknown) => TSearchSchema`
- Optional
- A function that will be called when this route is matched and passed the raw search params from the current location and return valid parsed search params. If this function throws, the route will be put into an error state and the error will be thrown during render. If this function does not throw, its return value will be used as the route's search params and the return type will be inferred into the rest of the router.
- A function that will be called when this route is matched and passed the parsed-but-not-yet-validated search params from the current location and return valid parsed search params. If this function throws, the route will be put into an error state and the error will be thrown during render. If this function does not throw, its return value will be used as the route's search params and the return type will be inferred into the rest of the router.
- By default, search params have already gone through the router's search parser before `validateSearch` runs. If you need the raw URL string values for validation, wrap your validator with `validateSearchWithRawInput(...)`.
- Optionally, the parameter type can be tagged with the `SearchSchemaInput` type like this: `(searchParams: TSearchSchemaInput & SearchSchemaInput) => TSearchSchema`. If this tag is present, `TSearchSchemaInput` will be used to type the `search` property of `<Link />` and `navigate()` **instead of** `TSearchSchema`. The difference between `TSearchSchemaInput` and `TSearchSchema` can be useful, for example, to express optional search parameters.

### `search.middlewares` property
Expand Down
18 changes: 18 additions & 0 deletions docs/router/guide/search-params.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,24 @@ In the above example, we're validating the search params of the `Route` and retu

The `validateSearch` option is a function that is provided the JSON parsed (but non-validated) search params as a `Record<string, unknown>` and returns a typed object of your choice. It's usually best to provide sensible fallbacks for malformed or unexpected search params so your users' experience stays non-interrupted.

If you need to validate against the raw URL string values instead of the default parsed search object, wrap the validator with `validateSearchWithRawInput(...)`:

```tsx
import {
createFileRoute,
validateSearchWithRawInput,
} from '@tanstack/react-router'
import { z } from 'zod'

export const Route = createFileRoute('/files')({
validateSearch: validateSearchWithRawInput(
z.object({
folder: z.string(),
}),
),
})
```

Here's an example:

```tsx title="src/routes/shop/products.tsx"
Expand Down
1 change: 1 addition & 0 deletions packages/react-router/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export {
createControlledPromise,
retainSearchParams,
stripSearchParams,
validateSearchWithRawInput,
createSerializationAdapter,
} from '@tanstack/router-core'

Expand Down
1 change: 1 addition & 0 deletions packages/router-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ export type {
} from './RouterProvider'

export { retainSearchParams, stripSearchParams } from './searchMiddleware'
export { validateSearchWithRawInput } from './searchValidator'

export {
defaultParseSearch,
Expand Down
11 changes: 7 additions & 4 deletions packages/router-core/src/qss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,22 @@ function toValue(str: unknown) {
* // Example input: decode("token=foo&key=value")
* // Expected output: { "token": "foo", "key": "value" }
*/
export function decode(str: any): any {
export function decode(
str: any,
parser: (value: string) => unknown = toValue,
): any {
const searchParams = new URLSearchParams(str)

const result: Record<string, unknown> = Object.create(null)

for (const [key, value] of searchParams.entries()) {
const previousValue = result[key]
if (previousValue == null) {
result[key] = toValue(value)
result[key] = parser(value)
} else if (Array.isArray(previousValue)) {
previousValue.push(toValue(value))
previousValue.push(parser(value))
} else {
result[key] = [previousValue, toValue(value)]
result[key] = [previousValue, parser(value)]
}
}

Expand Down
Loading