tanstack-table-search-params
v0.6.1
Published
React Hook for syncing TanStack Table state with URL search params
Downloads
8,955
Maintainers
Readme
TanStack Table Search Params
React Hook for syncing TanStack Table state with URL search params.
https://github.com/user-attachments/assets/1f1b4a65-fdec-4a80-a5d5-783642befaa3
🚀 Quick Start
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-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 examples below:
🔍 How it works
The useTableSearchParams
hook primarily does the following two things:
- Decode
query
(query parameter state) and return it as thestate
for Tanstack Table. - Return a function like
onChangeGlobalFilter
that encodesstate
as a query parameter and performsreplace
(orpush
).
⚙️ Options
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,
});
💬 Troubleshooting
Q. The page transitions every time the search params change
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 }),
});
Q. The value during IME conversion is set to search params
Create an input that supports IME conversion with a uncontrolled component.
Supported
List of supported TanStack table states
- [x] globalFilter
- [x] sorting
- [x] pagination
- [x] columnFilters
- [ ] columnOrder
- [ ] columnPinning
- [ ] columnSizing
- [ ] columnSizingInfo
- [ ] columnVisibility
- [ ] expanded
- [ ] grouping
- [ ] rowPinning
- [ ] rowSelection
Roadmap
- [ ] Support other table states
- [ ] Disable specific state
- [ ] Add
onChangeXxxQuery
option
TODO
- [ ] Add examples for other routers
- [ ] Add e2e tests
License
MIT