Tanstack Table is a highly customizable, headless UI library for building powerful tables in various frameworks, including React, Vue, and Solid.
I’ve developed a library called tanstack-table-search-params
. This library syncs TanStack Table's state—such as search and sorting—with URL parameters in React applications.
taro-28 / tanstack-table-search-params
React Hook for syncing TanStack Table state with URL search params.
TanStack Table Search Params
React Hook for syncing TanStack Table state with URL search params.
tanstack-table-search-params.mp4
🚀 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
…TanStack Table in React
Here’s a quick introduction to building tables with TanStack Table in React.
TanStack Table provides a useReactTable
hook, which takes in data and column definitions. It returns the table's state and handlers that you can use to render the table.
import {
createColumnHelper,
flexRender,
getCoreRowModel,
useReactTable,
} from "@tanstack/react-table";
type User = { id: string; name: string };
// Sample data
const data: User[] = [
{ id: "1", name: "John" },
{ id: "2", name: "Sara" },
];
// Column definitions
const columnHelper = createColumnHelper<User>();
const columns = [
columnHelper.accessor("id", {}),
columnHelper.accessor("name", {}),
];
export default function UserTable() {
// Create table
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
});
return (
<div>
<table>
<thead>
<tr>
{table.getFlatHeaders().map((header) => (
<th key={header.id}>
{flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</th>
))}
</tr>
</thead>
<tbody>
{/* Render table rows */}
{table.getRowModel().rows.map((row) => (
<tr key={row.id}>
{row.getAllCells().map((cell) => (
<td key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
);
}
Here is the resulting table:
Next, I implemented a search feature for this table, as shown below.
import {
// ...
+ getFilteredRowModel,
} from "@tanstack/react-table";
// ...
export default function UserTable() {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
+ getFilteredRowModel: getFilteredRowModel(),
});
return (
<div>
+ <input
+ placeholder="Search..."
+ value={table.getState().globalFilter}
+ onChange={(e) => table.setGlobalFilter(e.target.value)}
+ />
<table>
The table now supports basic search functionality:
Here, getCoreRowModel
and getFilteredRowModel
are functions that implement various table features called RowModels. In TanStack Table, you can use built-in RowModels like getFilteredRowModel
, or create custom RowModels to extend functionality, such as adding search or sorting capabilities to your table.
Introducing tanstack-table-search-params
tanstack-table-search-params
simplifies the synchronization of TanStack Table state with URL parameters. For example, in a Next.js Pages Router project, you only need to:
- Import and call
useTableSearchParams
, passing theuseRouter
object. - Pass the hook’s return value to
useReactTable
.
+ import { useRouter } from "next/router";
+ import { useTableSearchParams } from "tanstack-table-search-params";
// ...
export default function UserTable() {
+ const router = useRouter();
+ const stateAndOnChanges = useTableSearchParams(router);
const table = useReactTable({
+ ...stateAndOnChanges,
// ...
The result:
How It Works
To explain how tanstack-table-search-params
synchronizes the state of TanStack Table with URL parameters, we will modify the code above to pass only the necessary properties, instead of passing the entire router
object and stateAndOnChanges
object.
export default function UserTable() {
const { query, pathname, replace} = useRouter();
const stateAndOnChanges = useTableSearchParams({
query: router.query,
pathname: router.pathname,
replace: router.replace,
});
const table = useReactTable({
state: {
globalFilter: stateAndOnChanges.state.globalFilter,
},
onGlobalFilterChange: stateAndOnChanges.onGlobalFilterChange,
// ...
The reason for passing state.globalFilter
and onGlobalFilterChange
to useReactTable
is to configure the setup for managing TanStack Table state outside of useReactTable
.
https://tanstack.com/table/latest/docs/guide/global-filtering#global-filter-state
As shown in the code above, useTableSearchParams
takes the following:
-
query
: The React state for URL parameters -
pathname
: The current URL path -
replace
(orpush
): The function to replace (or push) the URL
and returns:
state.globalFilter
onGlobalFilterChange
As you can imagine from the arguments and return values, this hook is very simple in its functionality.
First, state
decodes the query
into the following TanStack Table TableState
type and returns it:
Next, onGlobalFilterChange
simply returns a function that encodes each part of the TanStack Table state into URL parameters and executes replace
.
Both the processes of creating state
and onGlobalFilterChange
are merely transformations (object → object, function → function), and neither of them holds any internal state. (The only exception is for debouncing, which will be discussed later, where internal state is used for debounce purposes.)
Supported TanStack Table States
Currently, the library supports the following four TanStack Table states, with plans to expand support in the future:
globalFilter
sorting
pagination
columnFilters
Supported Routers
As introduced in the How It Works, tanstack-table-search-params
simply converts the React state of URL parameters into TanStack Table state. This means it should (probably) work with any router that allows you to retrieve URL parameters as React state.
Examples are available for the following three routers. Feel free to check them out:
Additional Customization Options
With tanstack-table-search-params
, you can customize URL parameter names and encoding formats, etc.
URL Parameter Names
By default, URL parameter names match the names in the TableState
type, such as globalFilter
or sorting
.
To change the URL parameter names, use the paramNames
option as the second argument of useTableSearchParams
.
const stateAndOnChanges = useTableSearchParams(router, {
paramNames: {
// Change the URL parameter name
globalFilter: "search",
},
});
If you want to add a prefix or suffix to the parameter names, you can pass a function.
const stateAndOnChanges = useTableSearchParams(router, {
paramNames: {
// Add a prefix to the URL parameter name
globalFilter: (defaultName) => `userTable-${defaultName}`,
},
});
Encoding Formats
By default, the encoding format for URL parameter values is a straightforward format, as shown below:
State Value | Example URL Parameters |
---|---|
Global Search | ?globalFilter=John |
Sorting | ?sorting=name.desc |
Pagination | ?pageIndex=2&pageSize=20 |
To change the encoding format, you can specify custom encoders
and decoders
in the second argument of useTableSearchParams
.
const stateAndOnChanges = useTableSearchParams(router, {
encoders: {
// Change encoding format to use JSON.stringify
globalFilter: (globalFilter) => ({
globalFilter: JSON.stringify(globalFilter),
}),
},
decoders: {
globalFilter: (query) =>
query["globalFilter"]
? JSON.parse(query["globalFilter"])
: (query["globalFilter"] ?? ""),
},
});
You can also modify both the encoding format and parameter names.
const stateAndOnChanges = useTableSearchParams(router, {
encoders: {
sorting: (sorting) => ({
// Change encoding format to JSON.stringify and rename parameter to my-sorting
"my-sorting": JSON.stringify(sorting),
}),
},
decoders: {
sorting: (query) =>
query["my-sorting"]
? JSON.parse(query["my-sorting"])
: query["my-sorting"],
},
});
Debounce
You can debounce the synchronization of state with URL parameters.
const stateAndOnChanges = useTableSearchParams(router, {
debounceMilliseconds: 500,
});
It’s also possible to debounce only specific TanStack Table states.
const stateAndOnChanges = useTableSearchParams(router, {
debounceMilliseconds: {
// Debounce synchronization of globalFilter with URL parameters by 0.5 seconds
globalFilter: 500,
},
});
Default Values
You can specify default values for TanStack Table states when URL parameters are absent.
For example, without setting a default value for sorting, the state.sorting
value (sorting condition) and the corresponding URL parameter behave as follows:
state.sorting Value |
URL Parameter |
---|---|
[] |
None |
[{ id: "createdAt", desc: true }] |
?sorting=createdAt.desc |
[{ id: "createdAt", desc: false }] |
?sorting=createdAt.asc |
(Note: createdAt
is the column name.)
In contrast, if you set the default sorting order to descending as shown below:
const stateAndOnChanges = useTableSearchParams(router, {
defaultValues: {
sorting: [{ id: "createdAt", desc: true }],
},
});
The state.sorting
value and the corresponding URL parameter will behave as follows:
state.sorting Value |
URL Parameter |
---|---|
[] |
?sorting=none |
[{ id: "createdAt", desc: true }] |
None |
[{ id: "createdAt", desc: false }] |
?sorting=createdAt.asc |
In Conclusion
This library originated from my work, where I needed a simple way to sync TanStack Table states with URL parameters.
So far, I’ve implemented all the features I personally needed. Moving forward, I plan to enhance its completeness by supporting more TanStack Table states and expanding customization options.
By the way, this is the first time I’ve published one of my own packages on npm. It was a great experience to learn how to publish on npm and discover the convenience of tools like tsup.
I’d be thrilled if you gave it a try! (And I’d be even happier if you starred the repository!)
taro-28 / tanstack-table-search-params
React Hook for syncing TanStack Table state with URL search params.
TanStack Table Search Params
React Hook for syncing TanStack Table state with URL search params.
tanstack-table-search-params.mp4
🚀 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
…
Top comments (0)