tanstack-table-search-params
TypeScript icon, indicating that this package has built-in type declarations

0.6.0 • Public • Published

TanStack Table Search Params

NPM Version NPM Downloads GitHub Repo stars Bundlephobia Minzipped size

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 the state for Tanstack Table.
  • Return a function like onChangeGlobalFilter that encodes state as a query parameter and performs replace (or push).

⚙️ Options

🏷️ Custom query param name

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}`,
  },
});

🔢 Custom default value

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]);

🪄 Custom encoder/decoder

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),
        })),
  },
});

⏱️ Debounce

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,
});

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

Package Sidebar

Install

npm i tanstack-table-search-params

Weekly Downloads

1,332

Version

0.6.0

License

MIT

Unpacked Size

56.9 kB

Total Files

7

Last publish

Collaborators

  • taro-28