From a622648321f2f0c5f361fa007c16cff97018131e Mon Sep 17 00:00:00 2001 From: Marc Popescu-Pfeiffer Date: Sat, 18 Oct 2025 00:16:00 +0200 Subject: [PATCH] feat(docs): add TanStack Table parsers Add a new section in the docs covering how to use custom parsers with TanStack Table for filtering and sorting. --- .../community/tanstack-table.generator.tsx | 451 +++++++++++++++++- .../docs/parsers/community/tanstack-table.mdx | 22 +- 2 files changed, 463 insertions(+), 10 deletions(-) diff --git a/packages/docs/content/docs/parsers/community/tanstack-table.generator.tsx b/packages/docs/content/docs/parsers/community/tanstack-table.generator.tsx index d61d4ac9b..f33ec8409 100644 --- a/packages/docs/content/docs/parsers/community/tanstack-table.generator.tsx +++ b/packages/docs/content/docs/parsers/community/tanstack-table.generator.tsx @@ -23,7 +23,9 @@ import { parseAsIndex, parseAsInteger, parseAsString, - useQueryState + parseAsStringEnum, + useQueryState, + useQueryStates } from 'nuqs' import { useDeferredValue } from 'react' @@ -192,3 +194,450 @@ export function usePaginationSearchParams() { ) } + +export function TanStackTableSorting() { + const ACCESSOR_KEYS = ['name', 'email', 'role', 'status', 'createdAt'] + + const [sortColumnUrlKey, setSortColumnUrlKey] = useQueryState( + 'sortColumnUrlKey', + parseAsString.withDefault('sortColumn') + ) + const [sortDirectionUrlKey, setSortDirectionUrlKey] = useQueryState( + 'sortDirectionUrlKey', + parseAsString.withDefault('sortDirection') + ) + const [defaultSortColumn, setDefaultSortColumn] = useQueryState( + 'defaultSortColumn', + parseAsString.withDefault('name') + ) + const [urlSorting, setUrlSorting] = useQueryStates( + { + sortColumn: parseAsStringEnum(ACCESSOR_KEYS) + .withOptions({ + clearOnDefault: true + }) + .withDefault(defaultSortColumn), + sortDirection: parseAsStringEnum(['asc', 'desc']) + .withOptions({ + clearOnDefault: true + }) + .withDefault('desc') + }, + { + urlKeys: { + sortColumn: sortColumnUrlKey, + sortDirection: sortDirectionUrlKey + } + } + ) + + const sorting = urlSorting.sortColumn + ? [{ id: urlSorting.sortColumn, desc: urlSorting.sortDirection === 'desc' }] + : [] + + const parserCode = useDeferredValue(`import React from 'react' +import { parseAsStringEnum, useQueryStates } from 'nuqs' +import type { OnChangeFn, SortingState } from '@tanstack/react-table' + +export const useSortingSearchParams = ( + accessorKeys: string[], + defaultValue: string +): [sorting: SortingState, setSorting: OnChangeFn] => { + const [urlSorting, setUrlSorting] = useQueryStates( + { + sortColumn: parseAsStringEnum(accessorKeys) + .withOptions({ + clearOnDefault: true, + }) + .withDefault(defaultValue), + sortDirection: parseAsStringEnum(['asc', 'desc']) + .withOptions({ + clearOnDefault: true, + }) + .withDefault('desc'), + }, + { + urlKeys: { + sortColumn: '${sortColumnUrlKey}', + sortDirection: '${sortDirectionUrlKey}', + }, + }, + ) + + const sorting = React.useMemo( + () => + urlSorting.sortColumn + ? [{ id: urlSorting.sortColumn, desc: urlSorting.sortDirection === 'desc' }] + : [], + [urlSorting] + ) + + const setSorting = React.useCallback>( + (updaterOrValue) => { + const firstSortingColumn = + typeof updaterOrValue === 'function' + ? updaterOrValue(sorting)?.[0] + : updaterOrValue?.[0] + void setUrlSorting({ + sortColumn: firstSortingColumn?.id, + sortDirection: + firstSortingColumn === undefined + ? undefined + : firstSortingColumn.desc + ? 'desc' + : 'asc', + }) + }, + [setUrlSorting, sorting] + ) + + return [sorting, setSorting] +}`) + + const internalState = useDeferredValue(`[ + ${sorting.length > 0 ? `{ + id: '${sorting[0].id}', + desc: ${sorting[0].desc} + }` : '// No sorting applied'} +]`) + + return ( +
+
+ + +
+

+ Configure and copy-paste this parser into your application: +

+
+ + + + + } + className="flex-grow" + code={parserCode} + /> + +
+
+ ) +} + +export function TanStackTableFiltering() { + const ACCESSOR_KEYS = ['name', 'email', 'role', 'status', 'createdAt'] + + const [filterColumnUrlKey, setFilterColumnUrlKey] = useQueryState( + 'filterColumnUrlKey', + parseAsString.withDefault('filterColumn') + ) + const [filterValueUrlKey, setFilterValueUrlKey] = useQueryState( + 'filterValueUrlKey', + parseAsString.withDefault('filterValue') + ) + const [urlFilter, setUrlFilter] = useQueryStates( + { + filterColumn: parseAsStringEnum(ACCESSOR_KEYS), + filterValue: parseAsString + }, + { + urlKeys: { + filterColumn: filterColumnUrlKey, + filterValue: filterValueUrlKey + } + } + ) + + const filter = urlFilter.filterColumn + ? [{ id: urlFilter.filterColumn, value: urlFilter.filterValue }] + : [] + + const parserCode = useDeferredValue(`import React from 'react' +import { parseAsString, parseAsStringEnum, useQueryStates } from 'nuqs' +import type { ColumnFiltersState, OnChangeFn } from '@tanstack/react-table' + +export const useFilteredRowSearchParams = ( + accessorKeys: string[] +): [filter: ColumnFiltersState, setFilter: OnChangeFn] => { + const [urlFilter, setUrlFilter] = useQueryStates( + { + filterColumn: parseAsStringEnum(accessorKeys), + filterValue: parseAsString, + }, + { + urlKeys: { + filterColumn: '${filterColumnUrlKey}', + filterValue: '${filterValueUrlKey}', + }, + }, + ) + + const filter = React.useMemo( + () => + urlFilter.filterColumn + ? [{ id: urlFilter.filterColumn, value: urlFilter.filterValue }] + : [], + [urlFilter] + ) + + const setFilter = React.useCallback>( + (updaterOrValue) => { + const firstFilterColumn = + typeof updaterOrValue === 'function' + ? updaterOrValue(filter)?.[0] + : updaterOrValue?.[0] + void setUrlFilter({ + filterColumn: firstFilterColumn?.id, + filterValue: firstFilterColumn ? String(firstFilterColumn.value) : undefined, + }) + }, + [filter, setUrlFilter] + ) + + return [filter, setFilter] +}`) + + const internalState = useDeferredValue(`[ + ${filter.length > 0 ? `{ + id: '${filter[0].id}', + value: '${filter[0].value ?? ''}' + }` : '// No filter applied'} +]`) + + return ( +
+
+ + +
+

+ Configure and copy-paste this parser into your application: +

+
+ + + + + } + className="flex-grow" + code={parserCode} + /> + +
+
+ ) +} diff --git a/packages/docs/content/docs/parsers/community/tanstack-table.mdx b/packages/docs/content/docs/parsers/community/tanstack-table.mdx index a395551f9..ae8149bd4 100644 --- a/packages/docs/content/docs/parsers/community/tanstack-table.mdx +++ b/packages/docs/content/docs/parsers/community/tanstack-table.mdx @@ -19,20 +19,24 @@ TanStack Table stores pagination under two pieces of state: You will likely want the URL to follow your UI and be one-based for the page index: -import { TanStackTablePagination } from './tanstack-table.generator' +import { + TanStackTablePagination, + TanStackTableSorting, + TanStackTableFiltering +} from './tanstack-table.generator' -## Filtering +## Sorting - - This section is empty for now, [contributions](https://github.com/47ng/nuqs) are welcome! - + + + -## Sorting +## Filtering - - This section is empty for now, [contributions](https://github.com/47ng/nuqs) are welcome! - + + +