npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

tanstack-table-search-params

v0.6.1

Published

React Hook for syncing TanStack Table state with URL search params

Downloads

8,955

Readme

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

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