T
TanStack9mo ago
absent-sapphire

How to make default search params to not be populated when visiting page.

I have a page "/my-files" with a bunch of search params where each parameter have a default value. I have a link to this page looking like so: <Link to="/my-files">. When clicking on this, the URL get's populated with the default search params so it looks like this: /my-files?sort=a-z&page=1&pageSize=10&query=. I would like the URL to be kept as /my-files and only have the URL be populated when a search param is different than the default. Is what I want to do an anti-pattern of sorts? This is my route configuration:
const filesSortSchema = z.enum([
"newest",
"oldest",
"a-z",
"z-a",
"smallest",
"largest",
]);

export type FilesSort = z.infer<typeof filesSortSchema>;

const searchParamsSchema = z.object({
sort: filesSortSchema.default("a-z"),
page: z.number().int().min(1).default(1),
pageSize: z.number().int().min(10).default(10),
query: z.string().default(""),
});

export type MyFilesSearchParams = z.infer<typeof searchParamsSchema>;

export const Route = createFileRoute("/_authenticated/my-files/")({
component: RouteComponent,
validateSearch: zodValidator(searchParamsSchema),
});
const filesSortSchema = z.enum([
"newest",
"oldest",
"a-z",
"z-a",
"smallest",
"largest",
]);

export type FilesSort = z.infer<typeof filesSortSchema>;

const searchParamsSchema = z.object({
sort: filesSortSchema.default("a-z"),
page: z.number().int().min(1).default(1),
pageSize: z.number().int().min(10).default(10),
query: z.string().default(""),
});

export type MyFilesSearchParams = z.infer<typeof searchParamsSchema>;

export const Route = createFileRoute("/_authenticated/my-files/")({
component: RouteComponent,
validateSearch: zodValidator(searchParamsSchema),
});
10 Replies
flat-fuchsia
flat-fuchsia9mo ago
we have a concept named "search middlewares" exactly for that purpose
flat-fuchsia
flat-fuchsia9mo ago
Search Params | TanStack Router React Docs
Similar to how TanStack Query made handling server-state in your React applications a breeze, TanStack Router aims to unlock the power of URL search params in your applications. Why not just use URLSe...
flat-fuchsia
flat-fuchsia9mo ago
=> stripSearchParams
absent-sapphire
absent-sapphireOP9mo ago
oh, damn, I missed that in the bottom, thanks, I'll check it out.
flat-fuchsia
flat-fuchsia9mo ago
we have lots of features, so I can absolutely understand if they are bit hard to discover
absent-sapphire
absent-sapphireOP9mo ago
Cool, it works perfectly:
const filesSortSchema = z.enum([
"newest",
"oldest",
"a-z",
"z-a",
"smallest",
"largest",
]);

export type FilesSort = z.infer<typeof filesSortSchema>;

const defaultSearchParams = {
sort: "a-z",
page: 1,
pageSize: 10,
query: "",
} as const;

const searchParamsSchema = z.object({
sort: filesSortSchema.default(defaultSearchParams.sort),
page: z.number().int().min(1).default(defaultSearchParams.page),
pageSize: z.number().int().min(10).default(defaultSearchParams.pageSize),
query: z.string().default(defaultSearchParams.query),
});

export type MyFilesSearchParams = z.infer<typeof searchParamsSchema>;

export const Route = createFileRoute("/_authenticated/my-files/")({
component: RouteComponent,
validateSearch: zodValidator(searchParamsSchema),
search: {
middlewares: [stripSearchParams(defaultSearchParams)],
},
});
const filesSortSchema = z.enum([
"newest",
"oldest",
"a-z",
"z-a",
"smallest",
"largest",
]);

export type FilesSort = z.infer<typeof filesSortSchema>;

const defaultSearchParams = {
sort: "a-z",
page: 1,
pageSize: 10,
query: "",
} as const;

const searchParamsSchema = z.object({
sort: filesSortSchema.default(defaultSearchParams.sort),
page: z.number().int().min(1).default(defaultSearchParams.page),
pageSize: z.number().int().min(10).default(defaultSearchParams.pageSize),
query: z.string().default(defaultSearchParams.query),
});

export type MyFilesSearchParams = z.infer<typeof searchParamsSchema>;

export const Route = createFileRoute("/_authenticated/my-files/")({
component: RouteComponent,
validateSearch: zodValidator(searchParamsSchema),
search: {
middlewares: [stripSearchParams(defaultSearchParams)],
},
});
flat-fuchsia
flat-fuchsia9mo ago
great!
genetic-orange
genetic-orange2mo ago
import {
createFileRoute,
Link,
stripSearchParams,
} from "@tanstack/react-router";
import { zodValidator } from "@tanstack/zod-adapter";
import { z } from "zod/v4";

const defaultSearch = {
name: "world",
};

export const Route = createFileRoute("/hello")({
component: RouteComponent,
validateSearch: zodValidator(
z.object({
name: z.string().default(defaultSearch.name),
})
),
search: {
middlewares: [stripSearchParams(defaultSearch)],
},
});

function RouteComponent() {
return (
<div className="flex flex-col gap-4">
<Link from="/hello" search={{ name: "world" }} to=".">
{({ isActive }) => (
// URL will be "/hello"
// Will be active even if URL is "/hello?name=world2"
<div>
<h1>Go to "/hello?name=world"</h1>
<p>Is active: {isActive ? "true" : "false"}</p>
</div>
)}
</Link>
<Link from="/hello" search={{ name: "world2" }} to=".">
{({ isActive }) => (
// URL will be "/hello?name=world2"
// Will be active only if URL is "/hello?name=world2"
<div>
<h1>Go to "/hello?name=world2"</h1>
<p>Is active: {isActive ? "true" : "false"}</p>
</div>
)}
</Link>
</div>
);
}
import {
createFileRoute,
Link,
stripSearchParams,
} from "@tanstack/react-router";
import { zodValidator } from "@tanstack/zod-adapter";
import { z } from "zod/v4";

const defaultSearch = {
name: "world",
};

export const Route = createFileRoute("/hello")({
component: RouteComponent,
validateSearch: zodValidator(
z.object({
name: z.string().default(defaultSearch.name),
})
),
search: {
middlewares: [stripSearchParams(defaultSearch)],
},
});

function RouteComponent() {
return (
<div className="flex flex-col gap-4">
<Link from="/hello" search={{ name: "world" }} to=".">
{({ isActive }) => (
// URL will be "/hello"
// Will be active even if URL is "/hello?name=world2"
<div>
<h1>Go to "/hello?name=world"</h1>
<p>Is active: {isActive ? "true" : "false"}</p>
</div>
)}
</Link>
<Link from="/hello" search={{ name: "world2" }} to=".">
{({ isActive }) => (
// URL will be "/hello?name=world2"
// Will be active only if URL is "/hello?name=world2"
<div>
<h1>Go to "/hello?name=world2"</h1>
<p>Is active: {isActive ? "true" : "false"}</p>
</div>
)}
</Link>
</div>
);
}
Just discovering stripSearchParams and it seems exactly what I need! However, I'm finding that <Link> always is active even if the search param is different from the default. I can get around this by using exact: true but then that would break certain use cases. Not sure if this is expected or not, or if there is a different solution that would work better here.
flat-fuchsia
flat-fuchsia2mo ago
I think you could also do default in typescript vs in zod const { sort = "a-z". page = 1 ... } = Route.useSearch();
sensitive-blue
sensitive-blue2mo ago
Wanted to achieve something similar without luck. So finally I removed the "defaults" params

Did you find this page helpful?