
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
tanstack-table-search-params
Advanced tools
React Hook for syncing TanStack Table state with URL search params
React Hook for syncing TanStack Table state with URL search params.
https://github.com/user-attachments/assets/2a4108a2-254c-4c06-b346-f5d6e4c56c12
First, install the package.
npm i tanstack-table-search-params
For example, if you are using Next.js (Pages Router), you can use the hook like this.
import { useReactTable } from "@tanstack/react-table";
import { useRouter } from "next/router";
import { useTableSearchParams } from "tanstack-table-search-params";
const router = useRouter();
// Get state and onChanges
const stateAndOnChanges = useTableSearchParams({
query: router.query,
pathname: router.pathname,
replace: router.replace,
// or
push: router.push,
});
const table = useReactTable({
// Set state and onChanges
...stateAndOnChanges,
data,
columns,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getSortedRowModel: getSortedRowModel(),
// ... other options
});
Here is the demo.
Of course, you can use it with other routers.
Please refer to the following how to use:
** Use next/router **
import { useReactTable } from "@tanstack/react-table";
import { useTableSearchParams } from "tanstack-table-search-params";
import { useRouter } from "next/router";
const router = useRouter();
const stateAndOnChanges = useTableSearchParams(router);
const table = useReactTable({
// Set state and onChanges
...stateAndOnChanges,
data,
columns,
// ... other options
});
** Use next/navigation **
import { useReactTable } from "@tanstack/react-table";
import { useTableSearchParams } from "tanstack-table-search-params";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
const { replace } = useRouter();
const stateAndOnChanges = useTableSearchParams({
pathname: usePathname(),
query: useSearchParams(),
replace,
});
const table = useReactTable({
// Set state and onChanges
...stateAndOnChanges,
data,
columns,
// ... other options
});
import { useReactTable } from "@tanstack/react-table";
import { useTableSearchParams } from "tanstack-table-search-params";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
const { replace } = useRouter();
const stateAndOnChanges = useTableSearchParams({
pathname: usePathname(),
query: useSearchParams(),
replace,
});
const table = useReactTable({
// Set state and onChanges
...stateAndOnChanges,
data,
columns,
// ... other options
});
import { useReactTable } from "@tanstack/react-table";
import { useTableSearchParams } from "tanstack-table-search-params";
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/")({
component: Page,
});
function Page() {
const navigate = Route.useNavigate();
const query = Route.useSearch();
const stateAndOnChanges = useTableSearchParams({
replace: (url) => {
const searchParams = new URLSearchParams(url.split("?")[1]);
navigate({ search: Object.fromEntries(searchParams.entries()) });
},
query,
pathname: Route.path,
});
const table = useReactTable({
// Set state and onChanges
...stateAndOnChanges,
data,
columns,
});
// ...
}
import { useReactTable } from "@tanstack/react-table";
import { useTableSearchParams } from "tanstack-table-search-params";
import { useSearchParams, useNavigate, useLocation } from "react-router";
const [query] = useSearchParams();
const navigate = useNavigate();
const { pathname } = useLocation();
const stateAndOnChanges = useTableSearchParams({
query,
pathname,
replace: (url) => navigate(url, { replace: true }),
});
const table = useReactTable({
// Set state and onChanges
...stateAndOnChanges,
data,
columns,
// ... other options
});
The useTableSearchParams hook primarily does the following two things:
query (query parameter state) and return it as the state for Tanstack Table.onChangeGlobalFilter that encodes state as a query parameter and performs replace (or push).You can customize a query parameter name.
const stateAndOnChanges = useTableSearchParams(router, {
paramNames: {
// Customize query parameter name by passing a string
globalFilter: "userTable-globalFilter",
// Add prefix by passing a function
sorting: (defaultParamName) => `userTable-${defaultParamName}`,
},
});
You can customize the default value of a query parameter.
The "default value" is the value that is used as the state when the query parameter is not present.
const stateAndOnChanges = useTableSearchParams(router, {
defaultValues: {
// Sort by name in descending order when query parameter is not present
sorting: [{ id: "name", desc: true }],
},
});
If you want to set initial values for query parameters, either transition with the query parameter or add the query parameter after the transition, depending on the router you are using.
// Transition with the query parameter
<Link href={{ pathname: "/users", query: { globalFilter: "foo" } }}>
Users
</Link>;
// Add the query parameter after the transition
useEffect(() => {
router.replace({ query: { globalFilter: "foo" } });
}, [router.replace]);
You can customize the encoder/decoder for the query parameter.
const stateAndOnChanges = useTableSearchParams(router, {
// Use JSON.stringify/JSON.parse for encoding/decoding
encoders: {
// foo -> { "globalFilter": "foo" }
globalFilter: (globalFilter) => ({
globalFilter: JSON.stringify(globalFilter),
}),
},
decoders: {
// { "globalFilter": "foo" } -> foo
globalFilter: (query) =>
query["globalFilter"]
? JSON.parse(query["globalFilter"] as string)
: (query["globalFilter"] ?? ""),
},
});
// ...
const stateAndOnChanges = useTableSearchParams(router, {
// Encoders/decoders with different query parameter names can also be used.
encoders: {
// [{ id: "name", desc: true }] -> { "userTable-sorting": "[{ \"id\": \"name\", \"desc\": true }]" }
sorting: (sorting) => ({
"userTable-sorting": JSON.stringify(sorting),
}),
},
decoders: {
// { "userTable-sorting": "[{ \"id\": \"name\", \"desc\": true }]" } -> [{ id: "name", desc: true }]
sorting: (query) =>
query["userTable-sorting"]
? JSON.parse(query["userTable-sorting"] as string)
: query["userTable-sorting"],
},
});
// ...
const stateAndOnChanges = useTableSearchParams(router, {
// Encoders/decoders with different numbers of query parameters can also be used.
encoders: {
// [{ id: "name", value: "foo" }] -> { "columnFilters.name": "\"foo\"" }
columnFilters: (columnFilters) =>
Object.fromEntries(
columnFilters.map(({ id, value }) => [
`columnFilters.${id}`,
JSON.stringify(value),
]),
),
},
decoders: {
// { "columnFilters.name": "\"foo\"" } -> [{ id: "name", value: "foo" }]
columnFilters: (query) =>
Object.entries(query)
.filter(([key]) => key.startsWith("columnFilters."))
.map(([key, value]) => ({
id: key.replace("columnFilters.", ""),
value: JSON.parse(value as string),
})),
},
});
You can debounce the reflection of state changes in the query parameters.
const stateAndOnChanges = useTableSearchParams(router, {
debounceMilliseconds: {
// Debounce globalFilter by 500 milliseconds
globalFilter: 500,
},
});
Also, you can debounce all query parameters at once.
const stateAndOnChanges = useTableSearchParams(router, {
debounceMilliseconds: 500,
});
You can enable/disable the synchronization of a query parameter. All states are enabled by default.
const stateAndOnChanges = useTableSearchParams(router, {
// Disable synchronization of sorting
enabled: { sorting: false },
});
If you are using Next.js (Pages Router), you can prevent page transitions by using the shallow option.
const router = useRouter();
const stateAndOnChanges = useTableSearchParams({
...router,
replace: (query) => router.replace(query, undefined, { shallow: true }),
});
Create an input that supports IME conversion with a uncontrolled component.
To pass initial table state via the URL, call one of the encode* helpers from
tanstack-table-search-params/encoder-decoder and assign its return value to your link.
import { encodeSorting } from "tanstack-table-search-params/encoder-decoder";
<Link
href={{
pathname: "/some-page-with-table",
query: {
sorting: encodeSorting([{ id: "name", desc: true }]),
},
}}
>
foo
</Link>;
List of supported TanStack table states ([x] means supported, [ ] means not supported)
onChangeXxxQuery optionenabled with other optionsencoders/decoders with paramNames or defaultValuesMIT
FAQs
React Hook for syncing TanStack Table state with URL search params
We found that tanstack-table-search-params demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.