diff --git a/docs/devtools.md b/docs/devtools.md index 0b9b2d20e0..3066f30afb 100644 --- a/docs/devtools.md +++ b/docs/devtools.md @@ -61,7 +61,6 @@ The devtools identify each table by the `key` table option. Registration require const table = useTable({ key: 'users-table', // needed for devtools, omit if you don't want to use the devtools features, - rowModels: {}, columns, data, }) diff --git a/docs/framework/angular/guide/column-faceting.md b/docs/framework/angular/guide/column-faceting.md index 6696f412dd..62a9c5d967 100644 --- a/docs/framework/angular/guide/column-faceting.md +++ b/docs/framework/angular/guide/column-faceting.md @@ -14,19 +14,21 @@ Want to skip to the implementation? Check out these Angular examples: import { signal } from '@angular/core' import { injectTable, tableFeatures, columnFacetingFeature, columnFilteringFeature, createFacetedRowModel, createFacetedUniqueValues, createFacetedMinMaxValues, createFilteredRowModel, filterFns } from '@tanstack/angular-table' -const features = tableFeatures({ columnFacetingFeature, columnFilteringFeature }) +const features = tableFeatures({ + columnFacetingFeature, + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + facetedRowModel: createFacetedRowModel(), + facetedUniqueValues: createFacetedUniqueValues(), + facetedMinMaxValues: createFacetedMinMaxValues(), + filterFns, +}) export class App { readonly data = signal(defaultData) readonly table = injectTable(() => ({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - facetedRowModel: createFacetedRowModel(), - facetedUniqueValues: createFacetedUniqueValues(), - facetedMinMaxValues: createFacetedMinMaxValues(), - }, columns, data: this.data(), })) @@ -39,7 +41,7 @@ Faceting is a feature that generates lists of values from your table's data, eit ### Column Faceting Row Models -In order to use any of the column faceting features, add the `columnFacetingFeature` to your features and the appropriate faceted row models to `rowModels`. Faceting exists to power filter UIs, so in practice you will also register the `columnFilteringFeature` and a `filteredRowModel`. Without a filtered row model, the faceted row models fall back to the pre-filtered rows and the facet values will not react to other columns' filters. +In order to use any of the column faceting features, add the `columnFacetingFeature` and the appropriate faceted row model factories to your features. Faceting exists to power filter UIs, so in practice you will also register the `columnFilteringFeature` and a `filteredRowModel`. Without a filtered row model, the faceted row models fall back to the pre-filtered rows and the facet values will not react to other columns' filters. ```ts import { @@ -54,16 +56,18 @@ import { filterFns, } from '@tanstack/angular-table' -const features = tableFeatures({ columnFacetingFeature, columnFilteringFeature }) +const features = tableFeatures({ + columnFacetingFeature, + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), // facet values react to other columns' filters + facetedRowModel: createFacetedRowModel(), // required for faceting (other faceted row models depend on this) + facetedMinMaxValues: createFacetedMinMaxValues(), // if you need min/max values + facetedUniqueValues: createFacetedUniqueValues(), // if you need a list of unique values + filterFns, +}) readonly table = injectTable(() => ({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), // facet values react to other columns' filters - facetedRowModel: createFacetedRowModel(), // required for faceting (other faceted row models depend on this) - facetedMinMaxValues: createFacetedMinMaxValues(), // if you need min/max values - facetedUniqueValues: createFacetedUniqueValues(), // if you need a list of unique values - }, columns, data, })) @@ -113,39 +117,46 @@ const [min, max] = table.getGlobalFacetedMinMaxValues() ?? [0, 1]; ### Custom (Server-Side) Faceting -Instead of using the built-in client-side faceting features, you can implement your own faceting logic on the server-side and pass the faceted values to the client-side. Supply custom `rowModels.facetedUniqueValues` and `rowModels.facetedMinMaxValues` factories. Each factory receives the table and a column ID and returns a thunk that resolves the faceted values. The column instance APIs (`column.getFacetedUniqueValues()` and `column.getFacetedMinMaxValues()`) will then return your server-provided values. +Instead of using the built-in client-side faceting features, you can implement your own faceting logic on the server-side and pass the faceted values to the client-side. Supply custom `facetedUniqueValues` and `facetedMinMaxValues` factories in the `features` object. Each factory receives the table and a column ID and returns a thunk that resolves the faceted values. The column instance APIs (`column.getFacetedUniqueValues()` and `column.getFacetedMinMaxValues()`) will then return your server-provided values. ```ts readonly facetingQuery = injectQuery(() => ({ //... })) +const features = tableFeatures({ + columnFacetingFeature, + columnFilteringFeature, + facetedUniqueValues: (_table, columnId) => () => { + const uniqueValueMap = new Map() + //... populate from facetingQuery data for this columnId + return uniqueValueMap + }, + facetedMinMaxValues: (_table, columnId) => () => { + //... read from facetingQuery data for this columnId + return [min, max] + }, +}) + readonly table = injectTable(() => ({ features, - rowModels: { - facetedUniqueValues: (_table, columnId) => () => { - const uniqueValueMap = new Map() - //... populate from facetingQuery data for this columnId - return uniqueValueMap - }, - facetedMinMaxValues: (_table, columnId) => () => { - //... read from facetingQuery data for this columnId - return [min, max] - }, - }, columns, data, //... })) ``` -The same factories also serve global faceting. Global faceting requests values with the internal `__global__` column ID, so you can branch on it inside the same `facetedUniqueValues` and `facetedMinMaxValues` factories to return table-wide facet values: +The same factories also serve global faceting. Global faceting requests values with the internal `__global__` column ID, so you can branch on it inside the same `facetedUniqueValues` and `facetedMinMaxValues` factory functions to return table-wide facet values: ```ts -facetedUniqueValues: (_table, columnId) => () => { - if (columnId !== '__global__') return new Map() // per-column facets - return new Map(globalFacets.uniqueValues) // global facets -}, +const features = tableFeatures({ + columnFacetingFeature, + columnFilteringFeature, + facetedUniqueValues: (_table, columnId) => () => { + if (columnId !== '__global__') return new Map() // per-column facets + return new Map(globalFacets.uniqueValues) // global facets + }, +}) ``` Alternatively, you don't have to put any of your faceting logic through the TanStack Table APIs at all. Just fetch your lists and pass them to your filter components directly. diff --git a/docs/framework/angular/guide/column-filtering.md b/docs/framework/angular/guide/column-filtering.md index 809c79aab5..355a7e917f 100644 --- a/docs/framework/angular/guide/column-filtering.md +++ b/docs/framework/angular/guide/column-filtering.md @@ -16,16 +16,17 @@ Want to skip to the implementation? Check out these Angular examples: import { signal } from '@angular/core' import { injectTable, tableFeatures, columnFilteringFeature, createFilteredRowModel, filterFns } from '@tanstack/angular-table' -const features = tableFeatures({ columnFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) export class App { readonly data = signal(defaultData) readonly table = injectTable(() => ({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, columns, data: this.data(), })) @@ -60,14 +61,13 @@ If you're not sure, you can always start with client-side filtering and paginati If you have decided that you need to implement server-side filtering instead of using the built-in client-side filtering, here's how you do that. -No `filteredRowModel` is needed for manual server-side filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to `rowModels`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. +No `filteredRowModel` is needed for manual server-side filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to features, you can tell the table to skip it by setting the `manualFiltering` option to `true`. ```ts const features = tableFeatures({ columnFilteringFeature }) readonly table = injectTable(() => ({ features, - rowModels: {}, // no filteredRowModel needed for manual server-side filtering data, columns, manualFiltering: true, @@ -78,7 +78,7 @@ readonly table = injectTable(() => ({ ### Client-Side Filtering -If you are using the built-in client-side filtering features, add the `columnFilteringFeature` to your features and the `filteredRowModel` to your row models. Import `createFilteredRowModel` and `filterFns` from TanStack Table: +If you are using the built-in client-side filtering features, add the `columnFilteringFeature` and the `filteredRowModel` factory to your features. Import `createFilteredRowModel` and `filterFns` from TanStack Table: ```ts import { @@ -89,13 +89,14 @@ import { filterFns, } from '@tanstack/angular-table' -const features = tableFeatures({ columnFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) readonly table = injectTable(() => ({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, data, columns, })) @@ -124,7 +125,6 @@ You can access the column filter state from the table instance with `table.atoms ```ts readonly table = injectTable(() => ({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, //... @@ -148,7 +148,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data: this.data(), //... @@ -169,7 +168,6 @@ readonly columnFilters = signal([]) //... readonly table = injectTable(() => ({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data: this.data(), //... @@ -190,7 +188,6 @@ If you do not need to control the column filter state in your own state manageme ```ts readonly table = injectTable(() => ({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, //... @@ -278,33 +275,26 @@ const columns = [ } ] //... +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns: { + ...filterFns, + myCustomFilterFn: (row, columnId, filterValue) => { + return // true or false based on your custom logic + }, + startsWith: startsWithFilterFn, // defined elsewhere + }, +}) + readonly table = injectTable(() => ({ features, - rowModels: { - filteredRowModel: createFilteredRowModel({ - ...filterFns, - myCustomFilterFn: (row, columnId, filterValue) => { - return // true or false based on your custom logic - }, - startsWith: startsWithFilterFn, // defined elsewhere - }), - }, columns, data, })) ``` -> **TypeScript Note:** For `filterFn: 'myCustomFilterFn'` string references to typecheck, augment the `FilterFns` interface with a `declare module` block: -> -> ```ts -> declare module '@tanstack/angular-table' { -> interface FilterFns { -> myCustomFilterFn: FilterFn -> } -> } -> ``` -> -> Alternatively, skip the registry and the augmentation entirely by passing the function directly to the `filterFn` column option. See the [Fuzzy Search example](../examples/filters-fuzzy) for a complete registration with module augmentation. +> **TypeScript Note:** For `filterFn: 'myCustomFilterFn'` string references to typecheck, register the function in the `filterFns` slot on `tableFeatures` (as shown above). Alternatively, skip the registry entirely by passing the function directly to the `filterFn` column option. See the [Fuzzy Search example](../examples/filters-fuzzy) for a complete registration example. ##### Customize Filter Function Behavior @@ -356,7 +346,6 @@ const columns = [ //... readonly table = injectTable(() => ({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, enableColumnFilters: false, // disable column filtering for all columns @@ -374,14 +363,16 @@ By default, filtering is done from parent rows down, so if a parent row is filte However, if you want to allow sub-rows to be filtered and searched through, regardless of whether the parent row is filtered out, you can set the `filterFromLeafRows` table option to `true`. Setting this option to `true` will cause filtering to be done from leaf rows up, which means parent rows will be included so long as one of their child or grand-child rows is also included. ```ts -const features = tableFeatures({ columnFilteringFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnFilteringFeature, + rowExpandingFeature, + filteredRowModel: createFilteredRowModel(), + expandedRowModel: createExpandedRowModel(), + filterFns, +}) readonly table = injectTable(() => ({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - expandedRowModel: createExpandedRowModel(), - }, columns, data, filterFromLeafRows: true, // filter and search through sub-rows @@ -395,14 +386,16 @@ By default, filtering is done for all rows in a tree, no matter if they are root Use `maxLeafRowFilterDepth: 0` if you want to preserve a parent row's sub-rows from being filtered out while the parent row is passing the filter. ```ts -const features = tableFeatures({ columnFilteringFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnFilteringFeature, + rowExpandingFeature, + filteredRowModel: createFilteredRowModel(), + expandedRowModel: createExpandedRowModel(), + filterFns, +}) readonly table = injectTable(() => ({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - expandedRowModel: createExpandedRowModel(), - }, columns, data, maxLeafRowFilterDepth: 0, // only filter root level parent rows out diff --git a/docs/framework/angular/guide/column-ordering.md b/docs/framework/angular/guide/column-ordering.md index 3b29bb4030..adb1eec5ac 100644 --- a/docs/framework/angular/guide/column-ordering.md +++ b/docs/framework/angular/guide/column-ordering.md @@ -21,7 +21,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data: this.data(), })) @@ -55,7 +54,6 @@ const features = tableFeatures({ columnOrderingFeature }) readonly table = injectTable(() => ({ features, - rowModels: {}, //... initialState: { columnOrder: ['columnId1', 'columnId2', 'columnId3'], @@ -88,7 +86,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: {}, //... atoms: { columnOrder: this.columnOrderAtom, @@ -109,7 +106,6 @@ readonly columnOrder = signal(['columnId1', 'columnId2', 'colu //... readonly table = injectTable(() => ({ features, - rowModels: {}, //... state: { columnOrder: this.columnOrder(), @@ -138,7 +134,6 @@ const features = tableFeatures({ columnOrderingFeature }) export class App { readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data: this.data(), initialState: { diff --git a/docs/framework/angular/guide/column-pinning.md b/docs/framework/angular/guide/column-pinning.md index 36494770ec..eb96c6ac81 100644 --- a/docs/framework/angular/guide/column-pinning.md +++ b/docs/framework/angular/guide/column-pinning.md @@ -23,7 +23,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data: this.data(), })) @@ -65,7 +64,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: {}, //... atoms: { columnPinning: this.columnPinningAtom, @@ -87,7 +85,6 @@ readonly columnPinning = signal({ readonly table = injectTable(() => ({ features, - rowModels: {}, //... state: { columnPinning: this.columnPinning(), @@ -108,7 +105,6 @@ A very common use case is to pin some columns by default. You can do this by eit ```ts readonly table = injectTable(() => ({ features, - rowModels: {}, //... initialState: { columnPinning: { diff --git a/docs/framework/angular/guide/column-resizing.md b/docs/framework/angular/guide/column-resizing.md index 34a757a653..cf20e00d74 100644 --- a/docs/framework/angular/guide/column-resizing.md +++ b/docs/framework/angular/guide/column-resizing.md @@ -22,7 +22,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data: this.data(), })) @@ -63,7 +62,6 @@ const columns = [ readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data, })) @@ -179,7 +177,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data: this.data(), atoms: { @@ -205,7 +202,6 @@ readonly columnResizing = signal({ readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data, state: { diff --git a/docs/framework/angular/guide/column-sizing.md b/docs/framework/angular/guide/column-sizing.md index ef26a89b9e..1cfd7c32ef 100644 --- a/docs/framework/angular/guide/column-sizing.md +++ b/docs/framework/angular/guide/column-sizing.md @@ -21,7 +21,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data: this.data(), })) @@ -61,7 +60,6 @@ const columns = [ readonly table = injectTable(() => ({ features, - rowModels: {}, defaultColumn: { size: 200, // starting column size minSize: 50, // enforced during column resizing @@ -142,7 +140,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data: this.data(), atoms: { @@ -163,7 +160,6 @@ readonly columnSizing = signal({}) readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data: this.data(), state: { diff --git a/docs/framework/angular/guide/column-visibility.md b/docs/framework/angular/guide/column-visibility.md index aa1f45e44e..b08c312e11 100644 --- a/docs/framework/angular/guide/column-visibility.md +++ b/docs/framework/angular/guide/column-visibility.md @@ -21,7 +21,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data: this.data(), })) @@ -54,7 +53,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: {}, //... atoms: { columnVisibility: this.columnVisibilityAtom, @@ -78,7 +76,6 @@ readonly columnVisibility = signal({ readonly table = injectTable(() => ({ features, - rowModels: {}, //... state: { columnVisibility: this.columnVisibility(), @@ -100,7 +97,6 @@ const features = tableFeatures({ columnVisibilityFeature }) readonly table = injectTable(() => ({ features, - rowModels: {}, //... initialState: { columnVisibility: { diff --git a/docs/framework/angular/guide/composable-tables.md b/docs/framework/angular/guide/composable-tables.md index b61e038174..29a93b87b8 100644 --- a/docs/framework/angular/guide/composable-tables.md +++ b/docs/framework/angular/guide/composable-tables.md @@ -54,6 +54,11 @@ const features = tableFeatures({ columnFilteringFeature, rowPaginationFeature, rowSortingFeature, + sortedRowModel: createSortedRowModel(), + filteredRowModel: createFilteredRowModel(), + paginatedRowModel: createPaginatedRowModel(), + sortFns, + filterFns, }) export const { @@ -64,11 +69,6 @@ export const { injectTableHeaderContext, } = createTableHook({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - filteredRowModel: createFilteredRowModel(filterFns), - paginatedRowModel: createPaginatedRowModel(), - }, getRowId: (row) => row.id, tableComponents: { PaginationControls, @@ -99,7 +99,7 @@ This file is the source of truth for the feature set, row model pipeline, row ID | Helper | Purpose | |---|---| -| `injectAppTable` | Creates a table with the app's shared `features`, `rowModels`, defaults, and registered components already attached. | +| `injectAppTable` | Creates a table with the app's shared `features` (including row model factories), defaults, and registered components already attached. | | `createAppColumnHelper` | Creates column helpers where `cell`, `header`, and `footer` contexts know about the registered components. | | `injectTableContext` | Reads the current table inside registered table components like `PaginationControls`. | | `injectTableCellContext` | Reads the current cell inside registered cell components like `TextCell`. | diff --git a/docs/framework/angular/guide/custom-features.md b/docs/framework/angular/guide/custom-features.md index ec60b27ad0..b72e49705d 100644 --- a/docs/framework/angular/guide/custom-features.md +++ b/docs/framework/angular/guide/custom-features.md @@ -268,7 +268,6 @@ const features = tableFeatures({ densityPlugin }) readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data, //.. @@ -287,8 +286,7 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: {}, - columns, + columns, data: this.data(), //... state: { diff --git a/docs/framework/angular/guide/expanding.md b/docs/framework/angular/guide/expanding.md index b4ef9c1977..036606d078 100644 --- a/docs/framework/angular/guide/expanding.md +++ b/docs/framework/angular/guide/expanding.md @@ -14,16 +14,16 @@ Want to skip to the implementation? Check out these Angular examples: import { signal } from '@angular/core' import { injectTable, tableFeatures, rowExpandingFeature, createExpandedRowModel } from '@tanstack/angular-table' -const features = tableFeatures({ rowExpandingFeature }) +const features = tableFeatures({ + rowExpandingFeature, + expandedRowModel: createExpandedRowModel(), +}) export class App { readonly data = signal(defaultData) readonly table = injectTable(() => ({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, columns, data: this.data(), })) @@ -43,7 +43,7 @@ There are multiple use cases for expanding features in TanStack Table that will ### Enable Client-Side Expanding -To use the client-side expanding features, add the `rowExpandingFeature` to your features and the `expandedRowModel` to your row models: +To use the client-side expanding features, add the `rowExpandingFeature` and the `expandedRowModel` factory to your features: ```ts import { @@ -53,13 +53,13 @@ import { createExpandedRowModel, } from '@tanstack/angular-table' -const features = tableFeatures({ rowExpandingFeature }) +const features = tableFeatures({ + rowExpandingFeature, + expandedRowModel: createExpandedRowModel(), +}) readonly table = injectTable(() => ({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, // other options... })) ``` @@ -104,9 +104,6 @@ Then you can use the getSubRows function to return the children array in each ro ```ts readonly table = injectTable(() => ({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, getSubRows: (row) => row.children, // return the children array as sub-rows // other options... })) @@ -153,7 +150,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, // other options... atoms: { expanded: this.expandedAtom, // expanding APIs now update expandedAtom @@ -171,7 +167,6 @@ readonly expanded = signal({}) readonly table = injectTable(() => ({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, // other options... state: { expanded: this.expanded(), @@ -255,15 +250,17 @@ Use `table.setExpanded` to update the expanded state directly. `table.resetExpan By default, the filtering process starts from the parent rows and moves downwards. This means if a parent row is excluded by the filter, all its child rows will also be excluded. However, you can change this behavior by using the `filterFromLeafRows` option. When this option is enabled, the filtering process starts from the leaf (child) rows and moves upwards. This ensures that a parent row will be included in the filtered results as long as at least one of its child or grandchild rows meets the filter criteria. Additionally, you can control how deep into the child hierarchy the filter process goes by using the `maxLeafRowFilterDepth` option. This option allows you to specify the maximum depth of child rows that the filter should consider. ```ts -const features = tableFeatures({ columnFilteringFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnFilteringFeature, + rowExpandingFeature, + filteredRowModel: createFilteredRowModel(), + expandedRowModel: createExpandedRowModel(), + filterFns, +}) //... readonly table = injectTable(() => ({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - expandedRowModel: createExpandedRowModel(), - }, getSubRows: (row) => row.subRows, filterFromLeafRows: true, // search through the expanded rows maxLeafRowFilterDepth: 1, // limit the depth of the expanded rows that are searched @@ -278,7 +275,6 @@ By default, expanded rows are paginated along with the rest of the table (which ```ts readonly table = injectTable(() => ({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, // other options... paginateExpandedRows: false, })) @@ -299,16 +295,18 @@ If you are also using the grouping feature, the `expanded` state is automaticall A common reason to set `autoResetExpanded: false` is editing data while viewing the table (for example, inline cell editing). Every edit updates `data`, which recomputes the row models and would otherwise collapse the user's expanded rows. If you also use the pagination feature, pair it with `autoResetPageIndex: false` so the current page is kept as well. ```ts -const features = tableFeatures({ rowExpandingFeature, columnGroupingFeature }) +const features = tableFeatures({ + rowExpandingFeature, + columnGroupingFeature, + expandedRowModel: createExpandedRowModel(), + // the auto-reset only fires when the grouped row model recomputes + groupedRowModel: createGroupedRowModel(), + aggregationFns, +}) export class App { readonly table = injectTable(() => ({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - // the auto-reset only fires when the grouped row model recomputes - groupedRowModel: createGroupedRowModel(aggregationFns), - }, // other options... autoResetExpanded: false, // keep expanded state when data changes // autoResetAll: false, // or turn off all auto resets at once @@ -325,7 +323,6 @@ const features = tableFeatures({ rowExpandingFeature }) readonly table = injectTable(() => ({ features, - rowModels: {}, // no expandedRowModel needed for manual expanding // other options... manualExpanding: true, })) diff --git a/docs/framework/angular/guide/fuzzy-filtering.md b/docs/framework/angular/guide/fuzzy-filtering.md index f80b80b9b8..0ad05de134 100644 --- a/docs/framework/angular/guide/fuzzy-filtering.md +++ b/docs/framework/angular/guide/fuzzy-filtering.md @@ -12,12 +12,20 @@ Want to skip to the implementation? Check out these Angular examples: ```ts import { signal } from '@angular/core' -import { injectTable, tableFeatures, columnFilteringFeature, globalFilteringFeature, rowSortingFeature, createFilteredRowModel, createSortedRowModel, filterFns, sortFns } from '@tanstack/angular-table' +import { injectTable, tableFeatures, columnFilteringFeature, globalFilteringFeature, rowSortingFeature, createFilteredRowModel, createSortedRowModel, filterFns, sortFns, metaHelper } from '@tanstack/angular-table' +import type { RankingInfo } from '@tanstack/match-sorter-utils' + +interface FuzzyFilterMeta { itemRank?: RankingInfo } const features = tableFeatures({ columnFilteringFeature, globalFilteringFeature, rowSortingFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, // fuzzyFilter defined below + sortFns: { ...sortFns, fuzzy: fuzzySort }, // fuzzySort defined below + filterMeta: metaHelper(), }) export class App { @@ -25,10 +33,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data: this.data(), })) @@ -50,14 +54,21 @@ Using the match-sorter libraries is optional, but the TanStack Match Sorter Util ### Defining a Custom Fuzzy Filter Function -Here's an example of a custom fuzzy filter function: +First, define the filter meta shape and the features type that includes it: ```typescript import { rankItem } from '@tanstack/match-sorter-utils' import type { RankingInfo } from '@tanstack/match-sorter-utils' -import type { FilterFn, RowData } from '@tanstack/angular-table' +import type { FilterFn, TableFeatures, RowData } from '@tanstack/angular-table' + +interface FuzzyFilterMeta { itemRank?: RankingInfo } +type FuzzyFeatures = TableFeatures & { filterMeta: FuzzyFilterMeta } +``` -const fuzzyFilter: FilterFn = ( +Then define the fuzzy filter function using those types: + +```typescript +const fuzzyFilter: FilterFn = ( row, columnId, value, @@ -76,23 +87,25 @@ const fuzzyFilter: FilterFn = ( In this function, we're using the `rankItem` function from the `@tanstack/match-sorter-utils` library to rank the item. We then store the ranking information in the filter meta of the row (the `addMeta` callback is optional, so call it with optional chaining), and return whether the item passed the ranking criteria. -To reference this filter function by the string name `'fuzzy'` (and to type the stored filter meta), augment the `FilterFns` and `FilterMeta` interfaces with a `declare module` block: +Register the fuzzy filter and the filter meta slot in `tableFeatures` instead of using `declare module` augmentation: ```typescript -declare module '@tanstack/angular-table' { - // add the fuzzy filter to the filterFns registry types - interface FilterFns { - fuzzy: FilterFn - } - interface FilterMeta { - itemRank?: RankingInfo - } -} +import { metaHelper } from '@tanstack/angular-table' + +const features = tableFeatures({ + columnFilteringFeature, + globalFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + filterMeta: metaHelper(), +}) ``` +The `filterMeta` slot types the per-row filter metadata for this table. The `fuzzy` key in `filterFns` lets you reference the function by the string `'fuzzy'` in column `filterFn` options and `globalFilterFn`. + ### Using Fuzzy Filtering with Global Filtering -To use fuzzy filtering with global filtering, register the fuzzy filter function in the registry passed to `createFilteredRowModel` and reference it in the `globalFilterFn` option of the table: +To use fuzzy filtering with global filtering, register the fuzzy filter function in the `filterFns` slot of `tableFeatures` and reference it in the `globalFilterFn` option of the table: ```typescript import { @@ -105,23 +118,22 @@ import { createSortedRowModel, filterFns, sortFns, + metaHelper, } from '@tanstack/angular-table' const features = tableFeatures({ columnFilteringFeature, globalFilteringFeature, rowSortingFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), // needed if you want sorting with fuzzy rank + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + sortFns: { ...sortFns, fuzzy: fuzzySort }, + filterMeta: metaHelper(), }) readonly table = injectTable(() => ({ features, - rowModels: { - filteredRowModel: createFilteredRowModel({ - ...filterFns, - fuzzy: fuzzyFilter, - }), - sortedRowModel: createSortedRowModel(sortFns), // needed if you want sorting with fuzzy rank - }, columns, data, globalFilterFn: 'fuzzy', @@ -130,7 +142,7 @@ readonly table = injectTable(() => ({ ### Using Fuzzy Filtering with Column Filtering -To use fuzzy filtering with column filtering, pass your fuzzy filter function to `createFilteredRowModel` (merging it with the built-in `filterFns`). You can then specify the fuzzy filter by name in the `filterFn` option of the column definition: +To use fuzzy filtering with column filtering, register your fuzzy filter function in the `filterFns` slot of `tableFeatures` (as shown in the setup snippet above). You can then specify the fuzzy filter by name in the `filterFn` option of the column definition: ```typescript const column = [ @@ -156,7 +168,7 @@ import { compareItems } from '@tanstack/match-sorter-utils' import { sortFns } from '@tanstack/angular-table' import type { SortFn } from '@tanstack/angular-table' -const fuzzySort: SortFn = (rowA, rowB, columnId) => { +const fuzzySort: SortFn = (rowA, rowB, columnId) => { let dir = 0 // Only sort by rank if the column has ranking information @@ -187,4 +199,4 @@ You can then pass this sorting function directly to the `sortFn` option of the c } ``` -> **Note:** Unlike `filterFn: 'fuzzy'` above, `fuzzySort` is passed as a function rather than a string. A string reference like `sortFn: 'fuzzySort'` would only work if you also registered the function in the registry passed to `createSortedRowModel` (e.g. `createSortedRowModel({ ...sortFns, fuzzySort })`) and augmented the `SortFns` interface the same way as `FilterFns`. Passing the function directly skips both steps. +> **Note:** `fuzzySort` can also be referenced by the string `'fuzzy'` if it is registered in the `sortFns` slot of `tableFeatures` (as shown in the setup snippet above). Passing the function directly to `sortFn` skips the need to register it. diff --git a/docs/framework/angular/guide/global-filtering.md b/docs/framework/angular/guide/global-filtering.md index 2e275f6aac..d736f4c7de 100644 --- a/docs/framework/angular/guide/global-filtering.md +++ b/docs/framework/angular/guide/global-filtering.md @@ -13,18 +13,20 @@ Want to skip to the implementation? Check out these Angular examples: ```ts import { signal } from '@angular/core' -import { injectTable, tableFeatures, globalFilteringFeature, createFilteredRowModel, filterFns } from '@tanstack/angular-table' +import { injectTable, tableFeatures, columnFilteringFeature, globalFilteringFeature, createFilteredRowModel, filterFns } from '@tanstack/angular-table' -const features = tableFeatures({ globalFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + globalFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) export class App { readonly data = signal(defaultData) readonly table = injectTable(() => ({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, columns, data: this.data(), })) @@ -57,20 +59,20 @@ If you're not sure, you can always start with client-side filtering and paginati If you have decided that you need to implement server-side global filtering instead of using the built-in client-side global filtering, here's how you do that. -No `filteredRowModel` is needed for manual server-side global filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to `rowModels`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. +No `filteredRowModel` is needed for manual server-side global filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to features, you can tell the table to skip it by setting the `manualFiltering` option to `true`. ```ts import { injectTable, tableFeatures, + columnFilteringFeature, globalFilteringFeature, } from '@tanstack/angular-table' -const features = tableFeatures({ globalFilteringFeature }) +const features = tableFeatures({ columnFilteringFeature, globalFilteringFeature }) readonly table = injectTable(() => ({ features, - rowModels: {}, // no filteredRowModel needed for manual server-side global filtering data, columns, manualFiltering: true, @@ -81,38 +83,39 @@ Note: When using manual global filtering, many of the options that are discussed ### Client-Side Global Filtering -If you are using the built-in client-side global filtering, add the `globalFilteringFeature` to your features and the `filteredRowModel` to your row models: +If you are using the built-in client-side global filtering, add the `globalFilteringFeature` (along with its required `columnFilteringFeature` prerequisite) and the `filteredRowModel` factory to your features: ```ts import { injectTable, tableFeatures, + columnFilteringFeature, globalFilteringFeature, createFilteredRowModel, filterFns, } from '@tanstack/angular-table' -const features = tableFeatures({ globalFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + globalFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) readonly table = injectTable(() => ({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, // other options... })) ``` ### Global Filter Function -The `globalFilterFn` option allows you to specify the filter function that will be used for global filtering. The filter function can be a string that references a built-in filter function, a string that references a custom filter function registered in the registry passed to `createFilteredRowModel`, or a custom filter function passed directly. +The `globalFilterFn` option allows you to specify the filter function that will be used for global filtering. The filter function can be a string that references a built-in filter function, a string that references a custom filter function registered in the `filterFns` slot of `tableFeatures`, or a custom filter function passed directly. ```ts readonly table = injectTable(() => ({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, + // filteredRowModel and filterFns are registered in features data, columns, globalFilterFn: 'includesString', // built-in filter function @@ -150,7 +153,7 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, + // filteredRowModel and filterFns are registered in features // other options... atoms: { globalFilter: this.globalFilterAtom, // table.setGlobalFilter now updates globalFilterAtom @@ -169,7 +172,7 @@ readonly globalFilter = signal('') readonly table = injectTable(() => ({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, + // filteredRowModel and filterFns are registered in features // other options... state: { globalFilter: this.globalFilter(), @@ -207,7 +210,7 @@ const customFilterFn = (row, columnId, filterValue) => { readonly table = injectTable(() => ({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, + // filteredRowModel and filterFns are registered in features // other options... globalFilterFn: customFilterFn, })) @@ -220,7 +223,7 @@ If you want to set an initial global filter state when the table is initialized, ```ts readonly table = injectTable(() => ({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, + // filteredRowModel and filterFns are registered in features // other options... initialState: { globalFilter: 'search term', // if not controlling globalFilter state, set initial state here @@ -248,7 +251,7 @@ const columns = [ //... readonly table = injectTable(() => ({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, + // filteredRowModel and filterFns are registered in features // other options... columns, enableGlobalFilter: false, // disable global filtering for all columns diff --git a/docs/framework/angular/guide/grouping.md b/docs/framework/angular/guide/grouping.md index 6d7b659baa..41fc4a5d41 100644 --- a/docs/framework/angular/guide/grouping.md +++ b/docs/framework/angular/guide/grouping.md @@ -14,16 +14,17 @@ Want to skip to the implementation? Check out these Angular examples: import { signal } from '@angular/core' import { injectTable, tableFeatures, columnGroupingFeature, createGroupedRowModel, aggregationFns } from '@tanstack/angular-table' -const features = tableFeatures({ columnGroupingFeature }) +const features = tableFeatures({ + columnGroupingFeature, + groupedRowModel: createGroupedRowModel(), + aggregationFns, +}) export class App { readonly data = signal(defaultData) readonly table = injectTable(() => ({ features, - rowModels: { - groupedRowModel: createGroupedRowModel(aggregationFns), - }, columns, data: this.data(), })) @@ -40,7 +41,7 @@ Grouping can also affect column order. There are 3 table features that can reord 2. Manual [Column Ordering](./column-ordering) - A manually specified column order is applied. 3. **Grouping** - If grouping is enabled, a grouping state is active, and `tableOptions.groupedColumnMode` is set to `'reorder' | 'remove'`, then the grouped columns are reordered to the start of the column flow. -To use the grouping feature, add the `columnGroupingFeature` to your features and the `groupedRowModel` to your row models. The grouped row model is responsible for grouping the rows based on the grouping state. +To use the grouping feature, add the `columnGroupingFeature` and the `groupedRowModel` factory to your features. The grouped row model is responsible for grouping the rows based on the grouping state. ```ts import { @@ -51,13 +52,14 @@ import { aggregationFns, } from '@tanstack/angular-table' -const features = tableFeatures({ columnGroupingFeature }) +const features = tableFeatures({ + columnGroupingFeature, + groupedRowModel: createGroupedRowModel(), + aggregationFns, +}) readonly table = injectTable(() => ({ features, - rowModels: { - groupedRowModel: createGroupedRowModel(aggregationFns), - }, // other options... })) ``` @@ -66,14 +68,16 @@ When grouping state is active, the table will add matching rows as subRows to th To allow the user to expand and collapse the grouped rows, you can use the expanding feature. ```ts -const features = tableFeatures({ columnGroupingFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnGroupingFeature, + rowExpandingFeature, + groupedRowModel: createGroupedRowModel(), + expandedRowModel: createExpandedRowModel(), + aggregationFns, +}) readonly table = injectTable(() => ({ features, - rowModels: { - groupedRowModel: createGroupedRowModel(aggregationFns), - expandedRowModel: createExpandedRowModel(), - }, // other options... })) ``` @@ -97,7 +101,6 @@ By default, when a column is grouped, it is moved to the start of the table. You ```ts readonly table = injectTable(() => ({ features, - rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) }, // other options... groupedColumnMode: 'reorder', })) @@ -133,16 +136,19 @@ There are several built-in aggregation functions that you can use: You can define custom aggregation functions in the registry that you pass to `createGroupedRowModel`. The registry is a record where the keys are the names of the aggregation functions, and the values are the aggregation functions themselves. You can then reference these aggregation functions by name in a column's `aggregationFn` option. ```ts +const features = tableFeatures({ + columnGroupingFeature, + groupedRowModel: createGroupedRowModel(), + aggregationFns: { + ...aggregationFns, + myCustomAggregation: (columnId, leafRows, childRows) => { + // return the aggregated value + }, + }, +}) + readonly table = injectTable(() => ({ features, - rowModels: { - groupedRowModel: createGroupedRowModel({ - ...aggregationFns, - myCustomAggregation: (columnId, leafRows, childRows) => { - // return the aggregated value - }, - }), - }, // other options... })) ``` @@ -155,17 +161,7 @@ const column = columnHelper.accessor('key', { }) ``` -> **TypeScript Note:** For `aggregationFn: 'myCustomAggregation'` string references to typecheck, augment the `AggregationFns` interface with a `declare module` block: -> -> ```ts -> declare module '@tanstack/angular-table' { -> interface AggregationFns { -> myCustomAggregation: AggregationFn -> } -> } -> ``` -> -> Alternatively, skip the registry and the augmentation entirely by passing the function directly to the `aggregationFn` column option. +> **TypeScript Note:** For `aggregationFn: 'myCustomAggregation'` string references to typecheck, register the function in the `aggregationFns` slot on `tableFeatures` (as shown above). Alternatively, skip the registry entirely by passing the function directly to the `aggregationFn` column option. ### Manual Grouping @@ -176,7 +172,6 @@ const features = tableFeatures({ columnGroupingFeature }) readonly table = injectTable(() => ({ features, - rowModels: {}, // no groupedRowModel needed for manual grouping // other options... manualGrouping: true, })) @@ -197,7 +192,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) }, // other options... atoms: { grouping: this.groupingAtom, // grouping APIs now update groupingAtom @@ -215,7 +209,6 @@ readonly grouping = signal([]) readonly table = injectTable(() => ({ features, - rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) }, // other options... state: { grouping: this.grouping(), diff --git a/docs/framework/angular/guide/migrating.md b/docs/framework/angular/guide/migrating.md index 2e159e1378..0c9956d72e 100644 --- a/docs/framework/angular/guide/migrating.md +++ b/docs/framework/angular/guide/migrating.md @@ -2,14 +2,18 @@ title: Migrating to TanStack Table v9 (Angular) --- +> [!NOTE] +> `v9.0.0-beta.10` introduces a breaking change in how row models are defined in order to bring increased type-safety features. Row model factories and function registries now live as slots on the `features` object instead of a separate `rowModels` option, and the factories no longer take arguments. If you migrated on an earlier beta, see the [Row Model Factories](#row-model-factories) section below for the new shape. + ## What's New in TanStack Table v9 TanStack Table v9 is a major release that introduces significant architectural improvements while maintaining the core table logic you're familiar with. Here are the key changes: -### 1. Tree-shaking +### 1. Tree Shaking and Extensibility - **Features are tree-shakeable**: Features are now treated as plugins: import only what you use. If your table only needs sorting, you won't ship filtering, pagination, or other feature code. Bundlers can eliminate unused code, so for smaller tables you can expect a meaningfully smaller bundle compared to v8. This also lets TanStack Table add features over time without bloating everyone's bundles. -- **Row models and their functions are refactored**: Row model factories (`createFilteredRowModel`, `createSortedRowModel`, etc.) now accept their processing functions (`filterFns`, `sortFns`, `aggregationFns`) as parameters. This enables tree-shaking of the functions themselves: if you use a custom filter, you don't pay for built-in filters you never use. +- **Row models and their functions are refactored**: Row model factories (`createFilteredRowModel`, `createSortedRowModel`, etc.) are now slots on the `features` object, and their processing functions (`filterFns`, `sortFns`, `aggregationFns`) are registered as their own feature slots. This enables tree-shaking of the functions themselves: if you only register a custom filter, you don't pay for built-in filters you never use. +- **Custom feature plugins with full type safety**: The same plugin architecture that powers the built-in features is open to your own code. Write a custom feature with its own state, options, and APIs, register it in `tableFeatures()` alongside the built-ins, and the table's types pick it all up automatically. See the [Custom Features Guide](./custom-features.md). ### 2. State Management @@ -18,9 +22,15 @@ TanStack Table v9 is a major release that introduces significant architectural i ### 3. Composability -- **`tableOptions`**: New utilities let you compose and share table configurations. Define `features`, `rowModels`, and default options once, then reuse them across tables or pass them through `createTableHook`. +- **`tableOptions`**: New utilities let you compose and share table configurations. Define `features` (including row model factories) and default options once, then reuse them across tables or pass them through `createTableHook`. - **`createTableHook`** (optional, advanced): Create reusable, strongly typed Angular table factories with pre-bound features, row models, default options, and component registries. +### 4. Improved Type Safety (No More Declaration Merging) + +- **Function registries replace `declare module` augmentation**: Custom filter, sort, and aggregation functions are registered by name in the `filterFns` / `sortFns` / `aggregationFns` slots on `tableFeatures()`. The registered keys become the valid, type-safe string values for `filterFn`, `sortFn`, `globalFilterFn`, and `aggregationFn` in your column definitions, with full inference. No more augmenting the `FilterFns` / `SortFns` / `AggregationFns` interfaces globally. +- **Per-table meta slots**: The type-only `tableMeta`, `columnMeta`, and `filterMeta` slots declare meta types for a single table instead of merging into a global interface. The `filterMeta` slot types both the `addMeta` callback in filter functions and the values read back from `row.columnFiltersMeta`. +- **Feature-gated APIs and validated prerequisites**: APIs like `table.setSorting` only exist on the table type when their feature is registered, and `tableFeatures()` validates slot prerequisites at the type level. Registering `sortFns` without `rowSortingFeature`, or `globalFilteringFeature` without `columnFilteringFeature`, is a typed error instead of a silent runtime no-op. + ### The Good News: Most Upgrades Are Opt-in While v9 is a significant upgrade, **you don't have to adopt everything at once**: @@ -28,7 +38,7 @@ While v9 is a significant upgrade, **you don't have to adopt everything at once* - **Don't want to think about tree-shaking yet?** You can start with `stockFeatures` to include most commonly used features. - **Your table markup is largely unchanged.** How you render ``, ``, ``, ` ``` -Clicking a header now toggles between ascending, descending, and unsorted. Every other feature follows this same pattern: register the feature, register its row model if it has one, and use the APIs it adds to the table, columns, and rows. See the [Sorting Guide](./guide/sorting.md) and the [Sorting example](./examples/sorting) for custom sort functions, multi-sorting, and per-column options. +Clicking a header now toggles between ascending, descending, and unsorted. Every other feature follows this same pattern: register the feature and its row model factory (when it has one) inside `tableFeatures`, then use the APIs it adds to the table, columns, and rows. See the [Sorting Guide](./guide/sorting.md) and the [Sorting example](./examples/sorting) for custom sort functions, multi-sorting, and per-column options. ## Where to Go Next @@ -197,12 +194,13 @@ Clicking a header now toggles between ascending, descending, and unsorted. Every **Composable tables.** When multiple tables in your app share features, row models, and component conventions, define them once with `createTableHook`: ```ts -const features = tableFeatures({ rowSortingFeature }) - -const { injectAppTable, createAppColumnHelper } = createTableHook({ - features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, }) + +const { injectAppTable, createAppColumnHelper } = createTableHook({ features }) ``` See the [Composable Tables Guide](./guide/composable-tables.md) for the full pattern, including pre-bound cell and header components. diff --git a/docs/framework/angular/reference/functions/createTableHook.md b/docs/framework/angular/reference/functions/createTableHook.md index 2a2c09b91b..7d0c0184a2 100644 --- a/docs/framework/angular/reference/functions/createTableHook.md +++ b/docs/framework/angular/reference/functions/createTableHook.md @@ -9,7 +9,7 @@ title: createTableHook function createTableHook(__namedParameters): CreateTableHookResult; ``` -Defined in: [packages/angular-table/src/helpers/createTableHook.ts:368](https://github.com/TanStack/table/blob/main/packages/angular-table/src/helpers/createTableHook.ts#L368) +Defined in: [packages/angular-table/src/helpers/createTableHook.ts:364](https://github.com/TanStack/table/blob/main/packages/angular-table/src/helpers/createTableHook.ts#L364) Creates app-scoped Angular table helpers with features, row models, and renderable component maps pre-bound. @@ -51,7 +51,6 @@ helpers without repeating the same feature and component generics. ```ts const { injectAppTable, createAppColumnHelper } = createTableHook({ features, - rowModels: {}, tableComponents: {}, cellComponents: {}, headerComponents: {}, diff --git a/docs/framework/angular/reference/type-aliases/CreateTableHookResult.md b/docs/framework/angular/reference/type-aliases/CreateTableHookResult.md index 3b85f4e902..7f7d29348e 100644 --- a/docs/framework/angular/reference/type-aliases/CreateTableHookResult.md +++ b/docs/framework/angular/reference/type-aliases/CreateTableHookResult.md @@ -69,7 +69,7 @@ Defined in: [packages/angular-table/src/helpers/createTableHook.ts:335](https:// ##### tableOptions -() => `Omit`\<`TableOptions`\<`TFeatures`, `TData`\>, `"features"` \| `"rowModels"`\> +() => `Omit`\<`TableOptions`\<`TFeatures`, `TData`\>, `"features"`\> #### Returns diff --git a/docs/framework/lit/guide/column-faceting.md b/docs/framework/lit/guide/column-faceting.md index 992caebabb..971ac61d2f 100644 --- a/docs/framework/lit/guide/column-faceting.md +++ b/docs/framework/lit/guide/column-faceting.md @@ -15,7 +15,15 @@ import { LitElement, html } from 'lit' import { customElement, state } from 'lit/decorators.js' import { TableController, tableFeatures, columnFacetingFeature, columnFilteringFeature, createFacetedRowModel, createFacetedUniqueValues, createFacetedMinMaxValues, createFilteredRowModel, filterFns } from '@tanstack/lit-table' -const features = tableFeatures({ columnFacetingFeature, columnFilteringFeature }) +const features = tableFeatures({ + columnFacetingFeature, + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + facetedRowModel: createFacetedRowModel(), + facetedUniqueValues: createFacetedUniqueValues(), + facetedMinMaxValues: createFacetedMinMaxValues(), + filterFns, +}) @customElement('my-table') class MyTable extends LitElement { @@ -27,12 +35,6 @@ class MyTable extends LitElement { protected render() { const table = this.tableController.table({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - facetedRowModel: createFacetedRowModel(), - facetedUniqueValues: createFacetedUniqueValues(), - facetedMinMaxValues: createFacetedMinMaxValues(), - }, columns, data: this.data, }) @@ -48,7 +50,7 @@ Faceting is a feature that generates lists of values from your table's data, eit ### Column Faceting Row Models -In order to use any of the column faceting features, add the `columnFacetingFeature` to your features and the appropriate faceted row models to `rowModels`. Faceting exists to power filter UIs, so in practice you will also register the `columnFilteringFeature` and a `filteredRowModel`. Without a filtered row model, the faceted row models fall back to the pre-filtered rows and the facet values will not react to other columns' filters. +In order to use any of the column faceting features, add the `columnFacetingFeature` to your features and register the appropriate faceted row model factories as slots on `tableFeatures`. Faceting exists to power filter UIs, so in practice you will also register the `columnFilteringFeature` and a `filteredRowModel` slot. Without a filtered row model, the faceted row models fall back to the pre-filtered rows and the facet values will not react to other columns' filters. ```ts import { @@ -63,16 +65,18 @@ import { filterFns, } from '@tanstack/lit-table' -const features = tableFeatures({ columnFacetingFeature, columnFilteringFeature }) +const features = tableFeatures({ + columnFacetingFeature, + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), // facet values react to other columns' filters + facetedRowModel: createFacetedRowModel(), // required for faceting (other faceted row models depend on this) + facetedMinMaxValues: createFacetedMinMaxValues(), // if you need min/max values + facetedUniqueValues: createFacetedUniqueValues(), // if you need a list of unique values + filterFns, +}) const table = this.tableController.table({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), // facet values react to other columns' filters - facetedRowModel: createFacetedRowModel(), // required for faceting (other faceted row models depend on this) - facetedMinMaxValues: createFacetedMinMaxValues(), // if you need min/max values - facetedUniqueValues: createFacetedUniqueValues(), // if you need a list of unique values - }, columns, data: this.data, }) @@ -122,24 +126,27 @@ const [min, max] = table.getGlobalFacetedMinMaxValues() ?? [0, 1]; ### Custom (Server-Side) Faceting -Instead of using the built-in client-side faceting features, you can implement your own faceting logic on the server-side and pass the faceted values to the client-side. Supply custom `rowModels.facetedUniqueValues` and `rowModels.facetedMinMaxValues` factories. Each factory receives the table and a column ID and returns a thunk that resolves the faceted values. The column instance APIs (`column.getFacetedUniqueValues()` and `column.getFacetedMinMaxValues()`) will then return your server-provided values. +Instead of using the built-in client-side faceting features, you can implement your own faceting logic on the server-side and pass the faceted values to the client-side. Supply custom `facetedUniqueValues` and `facetedMinMaxValues` factories as slots on `tableFeatures`. Each factory receives the table and a column ID and returns a thunk that resolves the faceted values. The column instance APIs (`column.getFacetedUniqueValues()` and `column.getFacetedMinMaxValues()`) will then return your server-provided values. ```ts const serverFacets = await fetch('/api/faceting').then((res) => res.json()) +const features = tableFeatures({ + columnFacetingFeature, + columnFilteringFeature, + facetedUniqueValues: (_table, columnId) => () => { + const uniqueValueMap = new Map() + //... populate from serverFacets data for this columnId + return uniqueValueMap + }, + facetedMinMaxValues: (_table, columnId) => () => { + //... read from serverFacets data for this columnId + return [min, max] + }, +}) + const table = this.tableController.table({ features, - rowModels: { - facetedUniqueValues: (_table, columnId) => () => { - const uniqueValueMap = new Map() - //... populate from serverFacets data for this columnId - return uniqueValueMap - }, - facetedMinMaxValues: (_table, columnId) => () => { - //... read from serverFacets data for this columnId - return [min, max] - }, - }, columns, data: this.data, //... @@ -149,6 +156,7 @@ const table = this.tableController.table({ The same factories also serve global faceting. Global faceting requests values with the internal `__global__` column ID, so you can branch on it inside the same `facetedUniqueValues` and `facetedMinMaxValues` factories to return table-wide facet values: ```ts +// In the tableFeatures call: facetedUniqueValues: (_table, columnId) => () => { if (columnId !== '__global__') return new Map() // per-column facets return new Map(globalFacets.uniqueValues) // global facets diff --git a/docs/framework/lit/guide/column-filtering.md b/docs/framework/lit/guide/column-filtering.md index 158f9707b8..7a00f18c74 100644 --- a/docs/framework/lit/guide/column-filtering.md +++ b/docs/framework/lit/guide/column-filtering.md @@ -17,7 +17,11 @@ import { LitElement, html } from 'lit' import { customElement, state } from 'lit/decorators.js' import { TableController, tableFeatures, columnFilteringFeature, createFilteredRowModel, filterFns } from '@tanstack/lit-table' -const features = tableFeatures({ columnFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) @customElement('my-table') class MyTable extends LitElement { @@ -29,9 +33,6 @@ class MyTable extends LitElement { protected render() { const table = this.tableController.table({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, columns, data: this.data, }) @@ -69,14 +70,13 @@ If you're not sure, you can always start with client-side filtering and paginati If you have decided that you need to implement server-side filtering instead of using the built-in client-side filtering, here's how you do that. -No `filteredRowModel` is needed for manual server-side filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to `rowModels`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. +No `filteredRowModel` is needed for manual server-side filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have registered a `filteredRowModel` in your `features`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. ```ts const features = tableFeatures({ columnFilteringFeature }) const table = this.tableController.table({ features, - rowModels: {}, // no filteredRowModel needed for manual server-side filtering data: this.data, columns, manualFiltering: true, @@ -87,7 +87,7 @@ const table = this.tableController.table({ ### Client-Side Filtering -If you are using the built-in client-side filtering features, add the `columnFilteringFeature` to your features and the `filteredRowModel` to your row models. Import `createFilteredRowModel` and `filterFns` from TanStack Table: +If you are using the built-in client-side filtering features, add the `columnFilteringFeature` to your features and the `filteredRowModel` factory as a slot on `tableFeatures`. Import `createFilteredRowModel` and `filterFns` from TanStack Table: ```ts import { @@ -98,13 +98,14 @@ import { filterFns, } from '@tanstack/lit-table' -const features = tableFeatures({ columnFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) const table = this.tableController.table({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, data: this.data, columns, }) @@ -134,7 +135,6 @@ For reads in your `render` method, use `table.state.columnFilters` (the state se const table = this.tableController.table( { features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data: this.data, //... @@ -162,7 +162,6 @@ const columnFiltersAtom = createAtom([]) // can set initial // inside your element's render method const table = this.tableController.table({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data: this.data, //... @@ -182,7 +181,6 @@ private columnFilters: ColumnFiltersState = [] //... const table = this.tableController.table({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data: this.data, //... @@ -202,7 +200,6 @@ If you do not need to control the column filter state in your own state manageme ```ts const table = this.tableController.table({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data: this.data, //... @@ -238,13 +235,13 @@ By default there are 12 built-in filter functions to choose from: - `between` - Exclusive min/max range (blank endpoints are open-ended) - `betweenInclusive` - Inclusive min/max range (blank endpoints are open-ended) -You can also define your own custom filter functions, either inline as the `filterFn` column option, or by name in the filter function registry that you pass to `createFilteredRowModel`. +You can also define your own custom filter functions, either inline as the `filterFn` column option, or by name in the `filterFns` slot on `tableFeatures`. #### Custom Filter Functions > **Note:** These filter functions only run during client-side filtering. -Whether you register a custom filter function in the registry passed to `createFilteredRowModel` or pass it directly as a `filterFn` column option, it should have the following signature: +Whether you register a custom filter function in the `filterFns` slot on `tableFeatures` or pass it directly as a `filterFn` column option, it should have the following signature: ```ts const myCustomFilterFn: FilterFn = ( @@ -278,7 +275,7 @@ const columns = [ { header: () => 'Birthday', accessorKey: 'birthday', - filterFn: 'myCustomFilterFn', // reference a custom filter function registered with createFilteredRowModel + filterFn: 'myCustomFilterFn', // reference a custom filter function registered in the filterFns slot }, { header: () => 'Profile', @@ -290,33 +287,26 @@ const columns = [ } ] //... +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns: { + ...filterFns, + myCustomFilterFn: (row, columnId, filterValue) => { + return // true or false based on your custom logic + }, + startsWith: startsWithFilterFn, // defined elsewhere + }, +}) + const table = this.tableController.table({ features, - rowModels: { - filteredRowModel: createFilteredRowModel({ - ...filterFns, - myCustomFilterFn: (row, columnId, filterValue) => { - return // true or false based on your custom logic - }, - startsWith: startsWithFilterFn, // defined elsewhere - }), - }, columns, data: this.data, }) ``` -> **TypeScript Note:** For `filterFn: 'myCustomFilterFn'` string references to typecheck, augment the `FilterFns` interface with a `declare module` block: -> -> ```ts -> declare module '@tanstack/lit-table' { -> interface FilterFns { -> myCustomFilterFn: FilterFn -> } -> } -> ``` -> -> Alternatively, skip the registry and the augmentation entirely by passing the function directly to the `filterFn` column option. See the [Fuzzy Search example](../examples/filters-fuzzy) for a complete registration with module augmentation. +> **TypeScript Note:** String references like `filterFn: 'myCustomFilterFn'` are automatically typed when the function is registered in the `filterFns` slot on `tableFeatures`. The registry slot replaces the old `declare module` augmentation approach. Alternatively, skip the registry entirely by passing the function directly to the `filterFn` column option. See the [Fuzzy Search example](../examples/filters-fuzzy) for a complete registration example. ##### Customize Filter Function Behavior @@ -368,7 +358,6 @@ const columns = [ //... const table = this.tableController.table({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data: this.data, enableColumnFilters: false, // disable column filtering for all columns @@ -386,14 +375,16 @@ By default, filtering is done from parent rows down, so if a parent row is filte However, if you want to allow sub-rows to be filtered and searched through, regardless of whether the parent row is filtered out, you can set the `filterFromLeafRows` table option to `true`. Setting this option to `true` will cause filtering to be done from leaf rows up, which means parent rows will be included so long as one of their child or grand-child rows is also included. ```ts -const features = tableFeatures({ columnFilteringFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnFilteringFeature, + rowExpandingFeature, + filteredRowModel: createFilteredRowModel(), + expandedRowModel: createExpandedRowModel(), + filterFns, +}) const table = this.tableController.table({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - expandedRowModel: createExpandedRowModel(), - }, columns, data: this.data, filterFromLeafRows: true, // filter and search through sub-rows @@ -407,14 +398,16 @@ By default, filtering is done for all rows in a tree, no matter if they are root Use `maxLeafRowFilterDepth: 0` if you want to preserve a parent row's sub-rows from being filtered out while the parent row is passing the filter. ```ts -const features = tableFeatures({ columnFilteringFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnFilteringFeature, + rowExpandingFeature, + filteredRowModel: createFilteredRowModel(), + expandedRowModel: createExpandedRowModel(), + filterFns, +}) const table = this.tableController.table({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - expandedRowModel: createExpandedRowModel(), - }, columns, data: this.data, maxLeafRowFilterDepth: 0, // only filter root level parent rows out diff --git a/docs/framework/lit/guide/column-ordering.md b/docs/framework/lit/guide/column-ordering.md index fbcad7a9e9..322f21234f 100644 --- a/docs/framework/lit/guide/column-ordering.md +++ b/docs/framework/lit/guide/column-ordering.md @@ -27,7 +27,6 @@ class MyTable extends LitElement { protected render() { const table = this.tableController.table({ features, - rowModels: {}, columns, data: this.data, }) @@ -64,7 +63,6 @@ const features = tableFeatures({ columnOrderingFeature }) const table = this.tableController.table({ features, - rowModels: {}, //... initialState: { columnOrder: ['columnId1', 'columnId2', 'columnId3'], @@ -97,7 +95,6 @@ const columnOrderAtom = createAtom([ const table = this.tableController.table({ features, - rowModels: {}, //... atoms: { columnOrder: columnOrderAtom, @@ -118,7 +115,6 @@ private columnOrder: ColumnOrderState = ['columnId1', 'columnId2', 'columnId3'] //... const table = this.tableController.table({ features, - rowModels: {}, //... state: { columnOrder: this.columnOrder, diff --git a/docs/framework/lit/guide/column-pinning.md b/docs/framework/lit/guide/column-pinning.md index 1b2a94ed92..69620a0acb 100644 --- a/docs/framework/lit/guide/column-pinning.md +++ b/docs/framework/lit/guide/column-pinning.md @@ -29,7 +29,6 @@ class MyTable extends LitElement { protected render() { const table = this.tableController.table({ features, - rowModels: {}, columns, data: this.data, }) @@ -74,7 +73,6 @@ const columnPinningAtom = createAtom({ const table = this.tableController.table({ features, - rowModels: {}, //... atoms: { columnPinning: columnPinningAtom, @@ -96,7 +94,6 @@ private columnPinning: ColumnPinningState = { const table = this.tableController.table({ features, - rowModels: {}, //... state: { columnPinning: this.columnPinning, @@ -116,7 +113,6 @@ A very common use case is to pin some columns by default. You can do this by eit ```ts const table = this.tableController.table({ features, - rowModels: {}, //... initialState: { columnPinning: { diff --git a/docs/framework/lit/guide/column-resizing.md b/docs/framework/lit/guide/column-resizing.md index 5bb951fa30..eaaa07a1c0 100644 --- a/docs/framework/lit/guide/column-resizing.md +++ b/docs/framework/lit/guide/column-resizing.md @@ -28,7 +28,6 @@ class MyTable extends LitElement { protected render() { const table = this.tableController.table({ features, - rowModels: {}, columns, data: this.data, }) @@ -72,7 +71,6 @@ const columns = [ const table = this.tableController.table({ features, - rowModels: {}, columns, data: this.data, }) @@ -192,7 +190,6 @@ const columnResizingAtom = createAtom({ const table = this.tableController.table({ features, - rowModels: {}, columns, data: this.data, atoms: { @@ -216,7 +213,6 @@ private columnResizing: columnResizingState = { const table = this.tableController.table({ features, - rowModels: {}, columns, data: this.data, state: { diff --git a/docs/framework/lit/guide/column-sizing.md b/docs/framework/lit/guide/column-sizing.md index 508afc7cb8..de45f72970 100644 --- a/docs/framework/lit/guide/column-sizing.md +++ b/docs/framework/lit/guide/column-sizing.md @@ -27,7 +27,6 @@ class MyTable extends LitElement { protected render() { const table = this.tableController.table({ features, - rowModels: {}, columns, data: this.data, }) @@ -70,7 +69,6 @@ const columns = [ const table = this.tableController.table({ features, - rowModels: {}, defaultColumn: { size: 200, // starting column size minSize: 50, // enforced during column resizing @@ -151,7 +149,6 @@ const columnSizingAtom = createAtom({}) const table = this.tableController.table({ features, - rowModels: {}, columns, data: this.data, atoms: { @@ -172,7 +169,6 @@ private columnSizing: ColumnSizingState = {} const table = this.tableController.table({ features, - rowModels: {}, columns, data: this.data, state: { diff --git a/docs/framework/lit/guide/column-visibility.md b/docs/framework/lit/guide/column-visibility.md index 9a8eb564c5..f04f6c1c7b 100644 --- a/docs/framework/lit/guide/column-visibility.md +++ b/docs/framework/lit/guide/column-visibility.md @@ -27,7 +27,6 @@ class MyTable extends LitElement { protected render() { const table = this.tableController.table({ features, - rowModels: {}, columns, data: this.data, }) @@ -63,7 +62,6 @@ const columnVisibilityAtom = createAtom({ const table = this.tableController.table({ features, - rowModels: {}, //... atoms: { columnVisibility: columnVisibilityAtom, @@ -87,7 +85,6 @@ private columnVisibility: ColumnVisibilityState = { const table = this.tableController.table({ features, - rowModels: {}, //... state: { columnVisibility: this.columnVisibility, @@ -108,7 +105,6 @@ const features = tableFeatures({ columnVisibilityFeature }) const table = this.tableController.table({ features, - rowModels: {}, //... initialState: { columnVisibility: { diff --git a/docs/framework/lit/guide/composable-tables.md b/docs/framework/lit/guide/composable-tables.md index c73d08760f..34712a46d2 100644 --- a/docs/framework/lit/guide/composable-tables.md +++ b/docs/framework/lit/guide/composable-tables.md @@ -49,16 +49,16 @@ export const features = tableFeatures({ columnFilteringFeature, rowPaginationFeature, rowSortingFeature, + sortedRowModel: createSortedRowModel(), + filteredRowModel: createFilteredRowModel(), + paginatedRowModel: createPaginatedRowModel(), + sortFns, + filterFns, }) export const { createAppColumnHelper, useAppTable, useTableContext } = createTableHook({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - filteredRowModel: createFilteredRowModel(filterFns), - paginatedRowModel: createPaginatedRowModel(), - }, getRowId: (row) => row.id, cellComponents: { TextCell, diff --git a/docs/framework/lit/guide/custom-features.md b/docs/framework/lit/guide/custom-features.md index f357c7c872..5144c1eaa4 100644 --- a/docs/framework/lit/guide/custom-features.md +++ b/docs/framework/lit/guide/custom-features.md @@ -262,7 +262,6 @@ const features = tableFeatures({ densityPlugin }) const table = this.tableController.table({ features, - rowModels: {}, columns, data: this.data, //.. diff --git a/docs/framework/lit/guide/expanding.md b/docs/framework/lit/guide/expanding.md index eeb16dbf6e..99e48bd300 100644 --- a/docs/framework/lit/guide/expanding.md +++ b/docs/framework/lit/guide/expanding.md @@ -15,7 +15,10 @@ import { LitElement, html } from 'lit' import { customElement, state } from 'lit/decorators.js' import { TableController, tableFeatures, rowExpandingFeature, createExpandedRowModel } from '@tanstack/lit-table' -const features = tableFeatures({ rowExpandingFeature }) +const features = tableFeatures({ + rowExpandingFeature, + expandedRowModel: createExpandedRowModel(), +}) @customElement('my-table') class MyTable extends LitElement { @@ -27,9 +30,6 @@ class MyTable extends LitElement { protected render() { const table = this.tableController.table({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, columns, data: this.data, }) @@ -62,13 +62,13 @@ import { createExpandedRowModel, } from '@tanstack/lit-table' -const features = tableFeatures({ rowExpandingFeature }) +const features = tableFeatures({ + rowExpandingFeature, + expandedRowModel: createExpandedRowModel(), +}) const table = this.tableController.table({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, // other options... }) ``` @@ -113,9 +113,6 @@ Then you can use the getSubRows function to return the children array in each ro ```ts const table = this.tableController.table({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, getSubRows: (row) => row.children, // return the children array as sub-rows // other options... }) @@ -132,9 +129,6 @@ By default, the `row.getCanExpand()` row instance API will return false unless i ```ts const table = this.tableController.table({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, getRowCanExpand: (row) => true, // Add your logic to determine if a row can be expanded. True means all rows include expanded data // other options... }) @@ -174,7 +168,6 @@ const expandedAtom = createAtom({}) const table = this.tableController.table({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, // other options... atoms: { expanded: expandedAtom, // expanding APIs now update expandedAtom @@ -192,7 +185,6 @@ private expanded: ExpandedState = {} const table = this.tableController.table({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, // other options... state: { expanded: this.expanded, @@ -274,15 +266,17 @@ Use `table.setExpanded` to update the expanded state directly. `table.resetExpan By default, the filtering process starts from the parent rows and moves downwards. This means if a parent row is excluded by the filter, all its child rows will also be excluded. However, you can change this behavior by using the `filterFromLeafRows` option. When this option is enabled, the filtering process starts from the leaf (child) rows and moves upwards. This ensures that a parent row will be included in the filtered results as long as at least one of its child or grandchild rows meets the filter criteria. Additionally, you can control how deep into the child hierarchy the filter process goes by using the `maxLeafRowFilterDepth` option. This option allows you to specify the maximum depth of child rows that the filter should consider. ```ts -const features = tableFeatures({ columnFilteringFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnFilteringFeature, + rowExpandingFeature, + filteredRowModel: createFilteredRowModel(), + expandedRowModel: createExpandedRowModel(), + filterFns, +}) //... const table = this.tableController.table({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - expandedRowModel: createExpandedRowModel(), - }, getSubRows: (row) => row.subRows, filterFromLeafRows: true, // search through the expanded rows maxLeafRowFilterDepth: 1, // limit the depth of the expanded rows that are searched @@ -297,7 +291,6 @@ By default, expanded rows are paginated along with the rest of the table (which ```ts const table = this.tableController.table({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, // other options... paginateExpandedRows: false, }) @@ -320,7 +313,6 @@ A common reason to set `autoResetExpanded: false` is editing data while viewing ```ts const table = this.tableController.table({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, // other options... autoResetExpanded: false, // keep expanded state when data changes // autoResetAll: false, // or turn off all auto resets at once @@ -336,7 +328,6 @@ const features = tableFeatures({ rowExpandingFeature }) const table = this.tableController.table({ features, - rowModels: {}, // no expandedRowModel needed for manual expanding // other options... manualExpanding: true, }) diff --git a/docs/framework/lit/guide/fuzzy-filtering.md b/docs/framework/lit/guide/fuzzy-filtering.md index a857e56318..c9c72ece08 100644 --- a/docs/framework/lit/guide/fuzzy-filtering.md +++ b/docs/framework/lit/guide/fuzzy-filtering.md @@ -19,6 +19,10 @@ const features = tableFeatures({ columnFilteringFeature, globalFilteringFeature, rowSortingFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), + filterFns, + sortFns, }) @customElement('my-table') @@ -31,10 +35,6 @@ class MyTable extends LitElement { protected render() { const table = this.tableController.table({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data: this.data, }) @@ -64,9 +64,17 @@ Here's an example of a custom fuzzy filter function: ```typescript import { rankItem } from '@tanstack/match-sorter-utils' import type { RankingInfo } from '@tanstack/match-sorter-utils' -import type { FilterFn, RowData } from '@tanstack/lit-table' +import type { FilterFn, RowData, TableFeatures, metaHelper } from '@tanstack/lit-table' + +// Define the shape of filter meta stored by the fuzzy filter +interface FuzzyFilterMeta { + itemRank?: RankingInfo +} + +// Extend TableFeatures to attach the filterMeta slot for type inference +type FuzzyFeatures = TableFeatures & { filterMeta: FuzzyFilterMeta } -const fuzzyFilter: FilterFn = ( +const fuzzyFilter: FilterFn = ( row, columnId, value, @@ -85,23 +93,26 @@ const fuzzyFilter: FilterFn = ( In this function, we're using the `rankItem` function from the `@tanstack/match-sorter-utils` library to rank the item. We then store the ranking information in the filter meta of the row (the `addMeta` callback is optional, so call it with optional chaining), and return whether the item passed the ranking criteria. -To reference this filter function by the string name `'fuzzy'` (and to type the stored filter meta), augment the `FilterFns` and `FilterMeta` interfaces with a `declare module` block: +To reference this filter function by the string name `'fuzzy'` and type the stored filter meta, register it in the `filterFns` slot on `tableFeatures` and use the `filterMeta` slot: ```typescript -declare module '@tanstack/lit-table' { - // add the fuzzy filter to the filterFns registry types - interface FilterFns { - fuzzy: FilterFn - } - interface FilterMeta { - itemRank?: RankingInfo - } -} +import { metaHelper } from '@tanstack/lit-table' + +const features = tableFeatures({ + columnFilteringFeature, + globalFilteringFeature, + rowSortingFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + sortFns, + filterMeta: metaHelper(), +}) ``` ### Using Fuzzy Filtering with Global Filtering -To use fuzzy filtering with global filtering, register the fuzzy filter function in the registry passed to `createFilteredRowModel` and reference it in the `globalFilterFn` option of the table: +To use fuzzy filtering with global filtering, register the fuzzy filter function in the `filterFns` slot on `tableFeatures` and reference it in the `globalFilterFn` option of the table: ```typescript import { @@ -114,23 +125,22 @@ import { createSortedRowModel, filterFns, sortFns, + metaHelper, } from '@tanstack/lit-table' const features = tableFeatures({ columnFilteringFeature, globalFilteringFeature, rowSortingFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), // needed if you want sorting with fuzzy rank + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + sortFns, + filterMeta: metaHelper(), }) const table = this.tableController.table({ features, - rowModels: { - filteredRowModel: createFilteredRowModel({ - ...filterFns, - fuzzy: fuzzyFilter, - }), - sortedRowModel: createSortedRowModel(sortFns), // needed if you want sorting with fuzzy rank - }, columns, data: this.data, globalFilterFn: 'fuzzy', @@ -139,7 +149,7 @@ const table = this.tableController.table({ ### Using Fuzzy Filtering with Column Filtering -To use fuzzy filtering with column filtering, pass your fuzzy filter function to `createFilteredRowModel` (merging it with the built-in `filterFns`). You can then specify the fuzzy filter by name in the `filterFn` option of the column definition: +To use fuzzy filtering with column filtering, register the fuzzy filter in the `filterFns` slot on `tableFeatures`. You can then specify the fuzzy filter by name in the `filterFn` option of the column definition: ```typescript const column = [ @@ -165,7 +175,7 @@ import { compareItems } from '@tanstack/match-sorter-utils' import { sortFns } from '@tanstack/lit-table' import type { SortFn } from '@tanstack/lit-table' -const fuzzySort: SortFn = (rowA, rowB, columnId) => { +const fuzzySort: SortFn = (rowA, rowB, columnId) => { let dir = 0 // Only sort by rank if the column has ranking information @@ -196,4 +206,4 @@ You can then pass this sorting function directly to the `sortFn` option of the c } ``` -> **Note:** Unlike `filterFn: 'fuzzy'` above, `fuzzySort` is passed as a function rather than a string. A string reference like `sortFn: 'fuzzySort'` would only work if you also registered the function in the registry passed to `createSortedRowModel` (e.g. `createSortedRowModel({ ...sortFns, fuzzySort })`) and augmented the `SortFns` interface the same way as `FilterFns`. Passing the function directly skips both steps. +> **Note:** Unlike `filterFn: 'fuzzy'` above, `fuzzySort` is passed as a function rather than a string. A string reference like `sortFn: 'fuzzySort'` would only work if you also registered the function in the `sortFns` slot on `tableFeatures` (e.g. `sortFns: { ...sortFns, fuzzySort }`). Passing the function directly skips registration entirely. diff --git a/docs/framework/lit/guide/global-filtering.md b/docs/framework/lit/guide/global-filtering.md index d64c4adaa5..47c4306c60 100644 --- a/docs/framework/lit/guide/global-filtering.md +++ b/docs/framework/lit/guide/global-filtering.md @@ -14,9 +14,14 @@ Want to skip to the implementation? Check out these Lit examples: ```ts import { LitElement, html } from 'lit' import { customElement, state } from 'lit/decorators.js' -import { TableController, tableFeatures, globalFilteringFeature, createFilteredRowModel, filterFns } from '@tanstack/lit-table' +import { TableController, tableFeatures, columnFilteringFeature, globalFilteringFeature, createFilteredRowModel, filterFns } from '@tanstack/lit-table' -const features = tableFeatures({ globalFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + globalFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) @customElement('my-table') class MyTable extends LitElement { @@ -28,9 +33,6 @@ class MyTable extends LitElement { protected render() { const table = this.tableController.table({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, columns, data: this.data, }) @@ -66,20 +68,20 @@ If you're not sure, you can always start with client-side filtering and paginati If you have decided that you need to implement server-side global filtering instead of using the built-in client-side global filtering, here's how you do that. -No `filteredRowModel` is needed for manual server-side global filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to `rowModels`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. +No `filteredRowModel` is needed for manual server-side global filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have registered a `filteredRowModel` in your `features`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. ```ts import { TableController, tableFeatures, + columnFilteringFeature, globalFilteringFeature, } from '@tanstack/lit-table' -const features = tableFeatures({ globalFilteringFeature }) +const features = tableFeatures({ columnFilteringFeature, globalFilteringFeature }) const table = this.tableController.table({ features, - rowModels: {}, // no filteredRowModel needed for manual server-side global filtering data: this.data, columns, manualFiltering: true, @@ -90,38 +92,38 @@ Note: When using manual global filtering, many of the options that are discussed ### Client-Side Global Filtering -If you are using the built-in client-side global filtering, add the `globalFilteringFeature` to your features and the `filteredRowModel` to your row models: +If you are using the built-in client-side global filtering, add the `globalFilteringFeature` (along with its required `columnFilteringFeature` prerequisite) to your features and the `filteredRowModel` to your row models: ```ts import { TableController, tableFeatures, + columnFilteringFeature, globalFilteringFeature, createFilteredRowModel, filterFns, } from '@tanstack/lit-table' -const features = tableFeatures({ globalFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + globalFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) const table = this.tableController.table({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, // other options... }) ``` ### Global Filter Function -The `globalFilterFn` option allows you to specify the filter function that will be used for global filtering. The filter function can be a string that references a built-in filter function, a string that references a custom filter function registered in the registry passed to `createFilteredRowModel`, or a custom filter function passed directly. +The `globalFilterFn` option allows you to specify the filter function that will be used for global filtering. The filter function can be a string that references a built-in filter function, a string that references a custom filter function registered in the `filterFns` slot on `tableFeatures`, or a custom filter function passed directly. ```ts const table = this.tableController.table({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, data: this.data, columns, globalFilterFn: 'includesString', // built-in filter function @@ -160,7 +162,6 @@ const globalFilterAtom = createAtom('') // inside your element's render method const table = this.tableController.table({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... atoms: { globalFilter: globalFilterAtom, // table.setGlobalFilter now updates globalFilterAtom @@ -178,7 +179,6 @@ private globalFilter: string = '' const table = this.tableController.table({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... state: { globalFilter: this.globalFilter, @@ -217,7 +217,6 @@ const customFilterFn = (row, columnId, filterValue) => { const table = this.tableController.table({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... globalFilterFn: customFilterFn, }) @@ -230,7 +229,6 @@ If you want to set an initial global filter state when the table is initialized, ```ts const table = this.tableController.table({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... initialState: { globalFilter: 'search term', // if not controlling globalFilter state, set initial state here @@ -258,7 +256,6 @@ const columns = [ //... const table = this.tableController.table({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... columns, enableGlobalFilter: false, // disable global filtering for all columns diff --git a/docs/framework/lit/guide/grouping.md b/docs/framework/lit/guide/grouping.md index e4f9256eb8..03d4a4078a 100644 --- a/docs/framework/lit/guide/grouping.md +++ b/docs/framework/lit/guide/grouping.md @@ -15,7 +15,11 @@ import { LitElement, html } from 'lit' import { customElement, state } from 'lit/decorators.js' import { TableController, tableFeatures, columnGroupingFeature, createGroupedRowModel, aggregationFns } from '@tanstack/lit-table' -const features = tableFeatures({ columnGroupingFeature }) +const features = tableFeatures({ + columnGroupingFeature, + groupedRowModel: createGroupedRowModel(), + aggregationFns, +}) @customElement('my-table') class MyTable extends LitElement { @@ -27,9 +31,6 @@ class MyTable extends LitElement { protected render() { const table = this.tableController.table({ features, - rowModels: { - groupedRowModel: createGroupedRowModel(aggregationFns), - }, columns, data: this.data, }) @@ -60,13 +61,14 @@ import { aggregationFns, } from '@tanstack/lit-table' -const features = tableFeatures({ columnGroupingFeature }) +const features = tableFeatures({ + columnGroupingFeature, + groupedRowModel: createGroupedRowModel(), + aggregationFns, +}) const table = this.tableController.table({ features, - rowModels: { - groupedRowModel: createGroupedRowModel(aggregationFns), - }, // other options... }) ``` @@ -75,14 +77,16 @@ When grouping state is active, the table will add matching rows as subRows to th To allow the user to expand and collapse the grouped rows, you can use the expanding feature. ```ts -const features = tableFeatures({ columnGroupingFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnGroupingFeature, + rowExpandingFeature, + groupedRowModel: createGroupedRowModel(), + expandedRowModel: createExpandedRowModel(), + aggregationFns, +}) const table = this.tableController.table({ features, - rowModels: { - groupedRowModel: createGroupedRowModel(aggregationFns), - expandedRowModel: createExpandedRowModel(), - }, // other options... }) ``` @@ -106,7 +110,6 @@ By default, when a column is grouped, it is moved to the start of the table. You ```ts const table = this.tableController.table({ features, - rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) }, // other options... groupedColumnMode: 'reorder', }) @@ -114,7 +117,7 @@ const table = this.tableController.table({ ### Aggregations -When rows are grouped, you can aggregate the data in the grouped rows by columns using the `aggregationFn` column option. This is a string that is the name of a built-in aggregation function, or a custom aggregation function registered in the registry passed to `createGroupedRowModel`. +When rows are grouped, you can aggregate the data in the grouped rows by columns using the `aggregationFn` column option. This is a string that is the name of a built-in aggregation function, or a custom aggregation function registered in the `aggregationFns` slot on `tableFeatures`. ```ts const column = columnHelper.accessor('key', { @@ -139,19 +142,22 @@ There are several built-in aggregation functions that you can use: #### Custom Aggregations -You can define custom aggregation functions in the registry that you pass to `createGroupedRowModel`. The registry is a record where the keys are the names of the aggregation functions, and the values are the aggregation functions themselves. You can then reference these aggregation functions by name in a column's `aggregationFn` option. +You can define custom aggregation functions in the `aggregationFns` slot on `tableFeatures`. The slot is a record where the keys are the names of the aggregation functions and the values are the aggregation functions themselves. You can then reference these aggregation functions by name in a column's `aggregationFn` option. ```ts +const features = tableFeatures({ + columnGroupingFeature, + groupedRowModel: createGroupedRowModel(), + aggregationFns: { + ...aggregationFns, + myCustomAggregation: (columnId, leafRows, childRows) => { + // return the aggregated value + }, + }, +}) + const table = this.tableController.table({ features, - rowModels: { - groupedRowModel: createGroupedRowModel({ - ...aggregationFns, - myCustomAggregation: (columnId, leafRows, childRows) => { - // return the aggregated value - }, - }), - }, // other options... }) ``` @@ -164,17 +170,7 @@ const column = columnHelper.accessor('key', { }) ``` -> **TypeScript Note:** For `aggregationFn: 'myCustomAggregation'` string references to typecheck, augment the `AggregationFns` interface with a `declare module` block: -> -> ```ts -> declare module '@tanstack/lit-table' { -> interface AggregationFns { -> myCustomAggregation: AggregationFn -> } -> } -> ``` -> -> Alternatively, skip the registry and the augmentation entirely by passing the function directly to the `aggregationFn` column option. +> **TypeScript Note:** String references like `aggregationFn: 'myCustomAggregation'` are automatically typed when the function is registered in the `aggregationFns` slot on `tableFeatures`. The registry slot replaces the old `declare module` augmentation approach. Alternatively, skip the registry entirely by passing the function directly to the `aggregationFn` column option. ### Manual Grouping @@ -185,7 +181,6 @@ const features = tableFeatures({ columnGroupingFeature }) const table = this.tableController.table({ features, - rowModels: {}, // no groupedRowModel needed for manual grouping // other options... manualGrouping: true, }) @@ -206,7 +201,6 @@ const groupingAtom = createAtom([]) const table = this.tableController.table({ features, - rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) }, // other options... atoms: { grouping: groupingAtom, // grouping APIs now update groupingAtom @@ -224,7 +218,6 @@ private grouping: GroupingState = [] const table = this.tableController.table({ features, - rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) }, // other options... state: { grouping: this.grouping, diff --git a/docs/framework/lit/guide/migrating.md b/docs/framework/lit/guide/migrating.md index a0e2603fa8..10ef5c9e97 100644 --- a/docs/framework/lit/guide/migrating.md +++ b/docs/framework/lit/guide/migrating.md @@ -2,17 +2,21 @@ title: Migrating to TanStack Table v9 (Lit) --- +> [!NOTE] +> `v9.0.0-beta.10` introduces a breaking change in how row models are defined in order to bring increased type-safety features. Row model factories and function registries now live as slots on the `features` object instead of a separate `rowModels` option, and the factories no longer take arguments. If you migrated on an earlier beta, see the [Row Model and Function Registry Migration](#row-model-and-function-registry-migration) section below for the new shape. + ## What's New in TanStack Table v9 TanStack Table v9 is a major release with explicit feature registration, row model registration, and a new atom-backed state model. The Lit adapter wraps those APIs in a `ReactiveController`. > The Lit adapter may change during the v9 beta cycle. This guide documents the current local v9 API and avoids speculating about future beta changes. -### 1. Tree-shaking +### 1. Tree Shaking and Extensibility - **Features are tree-shakeable**: register only the table features you use. -- **Row models are explicit**: move root `get*RowModel` options into the `rowModels` object. -- **Function registries moved to factories**: row model factories receive `sortFns`, `filterFns`, and `aggregationFns` directly. +- **Row models are slots on features**: row model factories and function registries are slots on the `tableFeatures({...})` call instead of a separate `rowModels` option. +- **Function registries are feature slots**: `sortFns`, `filterFns`, and `aggregationFns` are registered on `tableFeatures` alongside the row model factories. +- **Custom feature plugins with full type safety**: The same plugin architecture that powers the built-in features is open to your own code. Write a custom feature with its own state, options, and APIs, register it in `tableFeatures()` alongside the built-ins, and the table's types pick it all up automatically. See the [Custom Features Guide](./custom-features.md). ### 2. State Management @@ -26,6 +30,12 @@ TanStack Table v9 is a major release with explicit feature registration, row mod - **`tableOptions()`**: compose reusable option fragments. - **`createTableHook()`**: define shared Lit table factories with pre-bound features, row models, defaults, and render helpers. +### 4. Improved Type Safety (No More Declaration Merging) + +- **Function registries replace `declare module` augmentation**: Custom filter, sort, and aggregation functions are registered by name in the `filterFns` / `sortFns` / `aggregationFns` slots on `tableFeatures()`. The registered keys become the valid, type-safe string values for `filterFn`, `sortFn`, `globalFilterFn`, and `aggregationFn` in your column definitions, with full inference. No more augmenting the `FilterFns` / `SortFns` / `AggregationFns` interfaces globally. +- **Per-table meta slots**: The type-only `tableMeta`, `columnMeta`, and `filterMeta` slots declare meta types for a single table instead of merging into a global interface. The `filterMeta` slot types both the `addMeta` callback in filter functions and the values read back from `row.columnFiltersMeta`. +- **Feature-gated APIs and validated prerequisites**: APIs like `table.setSorting` only exist on the table type when their feature is registered, and `tableFeatures()` validates slot prerequisites at the type level. Registering `sortFns` without `rowSortingFeature`, or `globalFilteringFeature` without `columnFilteringFeature`, is a typed error instead of a silent runtime no-op. + ### The Good News: Most Table Logic Is Still Familiar - Column definitions keep the same basic `accessorKey`, `accessorFn`, `header`, `cell`, and `footer` shapes. @@ -58,7 +68,6 @@ private tableController = new TableController(this) protected render() { const table = this.tableController.table({ features, - rowModels, columns, data: this.data, }) @@ -69,7 +78,7 @@ protected render() { The v9 controller takes the host only. Pass options to `.table(...)` during render. -### New Required Options: `features` and `rowModels` +### New Required Option: `features` ```ts // v8 @@ -97,7 +106,6 @@ private tableController = new TableController(this) protected render() { const table = this.tableController.table({ features, - rowModels: {}, // core row model is automatic columns, data: this.data, }) @@ -142,7 +150,6 @@ import { stockFeatures } from '@tanstack/lit-table' const table = this.tableController.table({ features: stockFeatures, - rowModels, columns, data: this.data, }) @@ -171,20 +178,20 @@ Use it as a temporary migration shortcut. Explicit feature registration is the p --- -## The `rowModels` Option +## Row Model and Function Registry Migration -Row models now live under `rowModels`. +Row model factories and function registries are now slots on `tableFeatures({...})`. The separate `rowModels` option is removed. ### Migration Mapping -| v8 Option | v9 `rowModels` Key | v9 Factory Function | +| v8 Option | v9 `tableFeatures` Slot | v9 Factory / Value | |---|---|---| | `getCoreRowModel()` | (automatic) | Not needed | -| `getFilteredRowModel()` | `filteredRowModel` | `createFilteredRowModel(filterFns)` | -| `getSortedRowModel()` | `sortedRowModel` | `createSortedRowModel(sortFns)` | +| `getFilteredRowModel()` + `filterFns` | `filteredRowModel` + `filterFns` | `createFilteredRowModel()` + `filterFns` | +| `getSortedRowModel()` + `sortingFns` | `sortedRowModel` + `sortFns` | `createSortedRowModel()` + `sortFns` | | `getPaginationRowModel()` | `paginatedRowModel` | `createPaginatedRowModel()` | | `getExpandedRowModel()` | `expandedRowModel` | `createExpandedRowModel()` | -| `getGroupedRowModel()` | `groupedRowModel` | `createGroupedRowModel(aggregationFns)` | +| `getGroupedRowModel()` + `aggregationFns` | `groupedRowModel` + `aggregationFns` | `createGroupedRowModel()` + `aggregationFns` | | `getFacetedRowModel()` | `facetedRowModel` | `createFacetedRowModel()` | | `getFacetedMinMaxValues()` | `facetedMinMaxValues` | `createFacetedMinMaxValues()` | | `getFacetedUniqueValues()` | `facetedUniqueValues` | `createFacetedUniqueValues()` | @@ -234,20 +241,18 @@ const features = tableFeatures({ columnFilteringFeature, rowPaginationFeature, rowSortingFeature, -}) - -const rowModels = { - filteredRowModel: createFilteredRowModel(filterFns), - sortedRowModel: createSortedRowModel(sortFns), + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), paginatedRowModel: createPaginatedRowModel(), -} + filterFns, + sortFns, +}) private tableController = new TableController(this) protected render() { const table = this.tableController.table({ features, - rowModels, columns, data: this.data, }) @@ -288,7 +293,6 @@ By default, `table.state` contains the full registered table state. ```ts const table = this.tableController.table({ features, - rowModels, columns, data: this.data, }) @@ -302,7 +306,6 @@ Pass a second-argument selector when you want `table.state` to contain only the const table = this.tableController.table( { features, - rowModels, columns, data: this.data, }, @@ -351,7 +354,6 @@ private pagination: PaginationState = { protected render() { const table = this.tableController.table({ features, - rowModels, columns, data: this.data, state: { @@ -391,7 +393,6 @@ const paginationAtom = createAtom({ protected render() { const table = this.tableController.table({ features, - rowModels, columns, data: this.data, atoms: { @@ -467,7 +468,6 @@ import { tableOptions } from '@tanstack/lit-table' const baseOptions = tableOptions({ features, - rowModels, defaultColumn: { minSize: 40, }, @@ -484,15 +484,12 @@ const table = this.tableController.table({ ## `createTableHook`: Composable Table Patterns -`createTableHook` creates shared Lit table helpers with features, row models, and render helpers already bound. +`createTableHook` creates shared Lit table helpers with features (including row model slots) and render helpers already bound. ```ts import { createTableHook } from '@tanstack/lit-table' -export const { useAppTable, createAppColumnHelper } = createTableHook({ - features, - rowModels, -}) +export const { useAppTable, createAppColumnHelper } = createTableHook({ features }) const columnHelper = createAppColumnHelper() @@ -613,6 +610,39 @@ const features = tableFeatures({ See the new [Table and Column Meta Guide](../../../guide/table-and-column-meta) for full details on both approaches. +### `FilterFns`/`SortFns`/`AggregationFns`/`FilterMeta` Augmentation Replaced by Registry Slots + +In v8, making a custom function usable as a string reference (like `filterFn: 'fuzzy'`) required `declare module` augmentation of the `FilterFns` interface, and typing filter meta required augmenting `FilterMeta`. In v9, registering the function in the matching registry slot does both jobs with no global augmentation: + +```ts +// v8 +declare module '@tanstack/lit-table' { + interface FilterFns { + fuzzy: FilterFn + } + interface FilterMeta { + itemRank: RankingInfo + } +} + +// v9 - register in the slot; the key becomes a valid string value +interface FuzzyFilterMeta { + itemRank?: RankingInfo +} + +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + filterMeta: metaHelper(), +}) + +// 'fuzzy' now typechecks in column defs for tables using these features +columnHelper.accessor('name', { filterFn: 'fuzzy' }) +``` + +The same pattern applies to `sortFns` (for `sortFn` string values) and `aggregationFns` (for `aggregationFn` string values). See the [Fuzzy Filtering Guide](./fuzzy-filtering.md) for a complete example. + ### `RowData` Type Restriction Prefer explicit object row types: @@ -632,9 +662,10 @@ type Person = { - [ ] Replace controller construction with `new TableController(this)`. - [ ] Move table options from the controller constructor into `tableController.table(...)`. - [ ] Define `features` using `tableFeatures()` (or use `stockFeatures`). -- [ ] Move root `get*RowModel` options into `rowModels`. +- [ ] Move row model factories and function registries into `tableFeatures({...})` slots (remove the separate `rowModels` option). - [ ] Remove `getCoreRowModel`; the core row model is automatic. -- [ ] Pass `sortFns`, `filterFns`, and `aggregationFns` to row model factories. +- [ ] Register `sortFns`, `filterFns`, and `aggregationFns` as slots on `tableFeatures({...})` (not as factory arguments). +- [ ] Replace `declare module` augmentation of `FilterFns`/`SortFns`/`AggregationFns` with registry-slot registration, and `FilterMeta` augmentation with the `filterMeta` slot. - [ ] Rename `sortingFn` to `sortFn`. - [ ] Add `typeof features` to column helpers and types. - [ ] Replace `table.getState()` reads with `table.state`, `table.store.state`, or `table.atoms..get()`. diff --git a/docs/framework/lit/guide/pagination.md b/docs/framework/lit/guide/pagination.md index 9cf23149e1..39ab2ca5a6 100644 --- a/docs/framework/lit/guide/pagination.md +++ b/docs/framework/lit/guide/pagination.md @@ -15,7 +15,10 @@ import { LitElement, html } from 'lit' import { customElement, state } from 'lit/decorators.js' import { TableController, tableFeatures, rowPaginationFeature, createPaginatedRowModel } from '@tanstack/lit-table' -const features = tableFeatures({ rowPaginationFeature }) +const features = tableFeatures({ + rowPaginationFeature, + paginatedRowModel: createPaginatedRowModel(), +}) @customElement('my-table') class MyTable extends LitElement { @@ -27,9 +30,6 @@ class MyTable extends LitElement { protected render() { const table = this.tableController.table({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data: this.data, }) @@ -77,13 +77,13 @@ import { createPaginatedRowModel, } from '@tanstack/lit-table' -const features = tableFeatures({ rowPaginationFeature }) +const features = tableFeatures({ + rowPaginationFeature, + paginatedRowModel: createPaginatedRowModel(), +}) const table = this.tableController.table({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data: this.data, }) @@ -110,7 +110,6 @@ const features = tableFeatures({ rowPaginationFeature }) const table = this.tableController.table({ features, - rowModels: {}, // no paginatedRowModel needed for server-side pagination columns, data: this.data, manualPagination: true, // turn off client-side pagination @@ -154,9 +153,6 @@ const paginationAtom = createAtom({ const table = this.tableController.table({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data: this.data, atoms: { @@ -178,9 +174,6 @@ private pagination: PaginationState = { const table = this.tableController.table({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data: this.data, onPaginationChange: (updater) => { @@ -197,9 +190,6 @@ Alternatively, if you have no need for managing the `pagination` state in your o ```ts const table = this.tableController.table({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data: this.data, initialState: { @@ -224,9 +214,6 @@ By default, `pageIndex` is reset to `0` whenever the client-side row models reco ```ts const table = this.tableController.table({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data: this.data, autoResetPageIndex: false, // turn off auto reset of pageIndex diff --git a/docs/framework/lit/guide/row-pinning.md b/docs/framework/lit/guide/row-pinning.md index d9c3273b95..fd9e41fb3c 100644 --- a/docs/framework/lit/guide/row-pinning.md +++ b/docs/framework/lit/guide/row-pinning.md @@ -27,7 +27,6 @@ class MyTable extends LitElement { protected render() { const table = this.tableController.table({ features, - rowModels: {}, columns, data: this.data, }) @@ -48,7 +47,7 @@ There are 2 table features that can reorder rows, which happen in the following ### Enable Row Pinning -To use row pinning, add `rowPinningFeature` to your features. Row pinning does not require a row model factory, so `rowModels` can stay empty unless your table uses other row-model features. +To use row pinning, add `rowPinningFeature` to your features. Row pinning does not require a row model factory; no row model slot is needed in `tableFeatures` unless your table uses other row-model features. ```ts import { @@ -61,7 +60,6 @@ const features = tableFeatures({ rowPinningFeature }) const table = this.tableController.table({ features, - rowModels: {}, columns, data: this.data, }) @@ -83,7 +81,6 @@ You can pin rows by default with `initialState.rowPinning`: ```ts const table = this.tableController.table({ features, - rowModels: {}, columns, data: this.data, initialState: { @@ -109,7 +106,6 @@ const rowPinningAtom = createAtom({ const table = this.tableController.table({ features, - rowModels: {}, columns, data: this.data, atoms: { @@ -131,7 +127,6 @@ private rowPinning: RowPinningState = { const table = this.tableController.table({ features, - rowModels: {}, columns, data: this.data, state: { @@ -222,7 +217,6 @@ By default, all rows can be pinned. You can disable row pinning for the whole ta ```ts const table = this.tableController.table({ features, - rowModels: {}, columns, data: this.data, enableRowPinning: row => row.original.status !== 'archived', @@ -238,7 +232,6 @@ Set `keepPinnedRows` to `false` if pinned rows should only render when they are ```ts const table = this.tableController.table({ features, - rowModels: {}, columns, data: this.data, keepPinnedRows: false, diff --git a/docs/framework/lit/guide/row-selection.md b/docs/framework/lit/guide/row-selection.md index 9f30755ae1..191d28fd2a 100644 --- a/docs/framework/lit/guide/row-selection.md +++ b/docs/framework/lit/guide/row-selection.md @@ -27,7 +27,6 @@ class MyTable extends LitElement { protected render() { const table = this.tableController.table({ features, - rowModels: {}, columns, data: this.data, }) @@ -77,7 +76,6 @@ const rowSelectionAtom = createAtom({}) const table = this.tableController.table({ features, - rowModels: {}, //... atoms: { rowSelection: rowSelectionAtom, // selection APIs now update rowSelectionAtom @@ -95,7 +93,6 @@ private rowSelection: RowSelectionState = {} const table = this.tableController.table({ features, - rowModels: {}, //... onRowSelectionChange: (updater) => { this.rowSelection = typeof updater === 'function' ? updater(this.rowSelection) : updater @@ -113,7 +110,6 @@ By default, the row id for each row is simply the `row.index`. If you are using ```ts const table = this.tableController.table({ features, - rowModels: {}, //... getRowId: (row) => row.uuid, // use the row's uuid from your database as the row id }) diff --git a/docs/framework/lit/guide/sorting.md b/docs/framework/lit/guide/sorting.md index b6438189d5..72a0e84349 100644 --- a/docs/framework/lit/guide/sorting.md +++ b/docs/framework/lit/guide/sorting.md @@ -16,7 +16,11 @@ import { LitElement, html } from 'lit' import { customElement, state } from 'lit/decorators.js' import { TableController, tableFeatures, rowSortingFeature, createSortedRowModel, sortFns } from '@tanstack/lit-table' -const features = tableFeatures({ rowSortingFeature }) +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, +}) @customElement('my-table') class MyTable extends LitElement { @@ -28,9 +32,6 @@ class MyTable extends LitElement { protected render() { const table = this.tableController.table({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data: this.data, }) @@ -66,7 +67,6 @@ For reads in your `render` method, use `table.state.sorting` (the state selected const table = this.tableController.table( { features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data: this.data, //... @@ -98,7 +98,6 @@ class MyTable extends LitElement { protected render() { const table = this.tableController.table({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data: this.data, //... @@ -122,7 +121,6 @@ private sorting: SortingState = [] //... const table = this.tableController.table({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data: this.data, //... @@ -142,7 +140,6 @@ If you do not need to control the sorting state in your own state management or ```ts const table = this.tableController.table({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data: this.data, //... @@ -176,7 +173,6 @@ const sortingAtom = createAtom([]) //... const table = this.tableController.table({ features, - rowModels: {}, // no sortedRowModel needed for manual sorting columns, data: this.data, manualSorting: true, // use pre-sorted row model instead of sorted row model @@ -205,13 +201,14 @@ import { sortFns, } from '@tanstack/lit-table' -const features = tableFeatures({ rowSortingFeature }) +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, +}) const table = this.tableController.table({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data: this.data, }) @@ -232,11 +229,11 @@ By default, there are 6 built-in sorting functions to choose from: - `datetime` - Sorts by time, use this if your values are `Date` objects. - `basic` - Sorts using a basic/standard `a > b ? 1 : a < b ? -1 : 0` comparison. This is the fastest sorting function, but may not be the most accurate. -You can also define your own custom sorting functions, either inline as the `sortFn` column option, or by name in the sorting function registry that you pass to `createSortedRowModel`. +You can also define your own custom sorting functions, either inline as the `sortFn` column option, or by name in the `sortFns` slot on `tableFeatures`. #### Custom Sorting Functions -Whether you register a custom sorting function in the registry passed to `createSortedRowModel` or pass it directly as a `sortFn` column option, it should have the following signature: +Whether you register a custom sorting function in the `sortFns` slot on `tableFeatures` or pass it directly as a `sortFn` column option, it should have the following signature: ```ts //optionally use the SortFn to infer the parameter types @@ -265,7 +262,7 @@ const columns = [ { header: () => 'Age', accessorKey: 'age', - sortFn: 'myCustomSortFn', // reference a custom sorting function registered with createSortedRowModel + sortFn: 'myCustomSortFn', // reference a custom sorting function registered in the sortFns slot }, { header: () => 'Birthday', @@ -282,35 +279,28 @@ const columns = [ } ] //... +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns: { + ...sortFns, + myCustomSortFn: (rowA, rowB, columnId) => + rowA.original[columnId] > rowB.original[columnId] + ? 1 + : rowA.original[columnId] < rowB.original[columnId] + ? -1 + : 0, + }, +}) + const table = this.tableController.table({ features, - rowModels: { - sortedRowModel: createSortedRowModel({ - ...sortFns, - myCustomSortFn: (rowA, rowB, columnId) => - rowA.original[columnId] > rowB.original[columnId] - ? 1 - : rowA.original[columnId] < rowB.original[columnId] - ? -1 - : 0, - }), - }, columns, data: this.data, }) ``` -> **TypeScript Note:** For `sortFn: 'myCustomSortFn'` string references to typecheck, augment the `SortFns` interface with a `declare module` block: -> -> ```ts -> declare module '@tanstack/lit-table' { -> interface SortFns { -> myCustomSortFn: SortFn -> } -> } -> ``` -> -> Alternatively, skip the registry and the augmentation entirely by passing the function directly to the `sortFn` column option. +> **TypeScript Note:** String references like `sortFn: 'myCustomSortFn'` are automatically typed when the function is registered in the `sortFns` slot on `tableFeatures`. The registry slot replaces the old `declare module` augmentation approach. Alternatively, skip the registry entirely by passing the function directly to the `sortFn` column option. ### Customize Sorting @@ -336,7 +326,6 @@ const columns = [ //... const table = this.tableController.table({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data: this.data, enableSorting: false, // disable sorting for the entire table @@ -364,7 +353,6 @@ const columns = [ //... const table = this.tableController.table({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data: this.data, sortDescFirst: true, //sort by all columns in descending order first (default is ascending for string columns and descending for number columns) @@ -431,7 +419,6 @@ Once a column is sorted and `enableSortingRemoval` is `false`, toggling the sort ```ts const table = this.tableController.table({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data: this.data, enableSortingRemoval: false, // disable the ability to remove sorting on columns (sorting can never return to 'none' once applied) @@ -458,7 +445,6 @@ const columns = [ //... const table = this.tableController.table({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data: this.data, enableMultiSort: false, // disable multi-sorting for the entire table @@ -472,7 +458,6 @@ By default, the `Shift` key is used to trigger multi-sorting. You can change thi ```ts const table = this.tableController.table({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data: this.data, isMultiSortEvent: (e) => true, // normal click triggers multi-sorting @@ -488,7 +473,6 @@ By default, there is no limit to the number of columns that can be sorted at onc ```ts const table = this.tableController.table({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data: this.data, maxMultiSortColCount: 3, // only allow 3 columns to be sorted at once @@ -502,7 +486,6 @@ By default, the ability to remove multi-sorts is enabled. You can disable this b ```ts const table = this.tableController.table({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data: this.data, enableMultiRemove: false, // disable the ability to remove multi-sorts diff --git a/docs/framework/lit/guide/table-state.md b/docs/framework/lit/guide/table-state.md index 47b0c9d049..aceb1f4902 100644 --- a/docs/framework/lit/guide/table-state.md +++ b/docs/framework/lit/guide/table-state.md @@ -44,14 +44,13 @@ State slices are only created for the features that are registered in `features` const features = tableFeatures({ rowPaginationFeature, rowSortingFeature, + paginatedRowModel: createPaginatedRowModel(), + sortedRowModel: createSortedRowModel(), + sortFns, }) const table = this.tableController.table({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data: this._data, }) @@ -99,9 +98,6 @@ The second argument to `tableController.table(...)` is a TanStack Store selector const table = this.tableController.table( { features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data: this._data, }, @@ -165,10 +161,6 @@ If you only need to customize the starting value for some table state, use `init ```ts const table = this.tableController.table({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), - }, columns, data: this._data, initialState: { @@ -228,7 +220,6 @@ const paginationAtom = createAtom({ const table = this.tableController.table({ features, - rowModels: {}, columns, data: this._data, atoms: { @@ -253,9 +244,6 @@ private _sorting: SortingState = [] protected render() { const table = this.tableController.table({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data: this._data, state: { diff --git a/docs/framework/lit/guide/virtualization.md b/docs/framework/lit/guide/virtualization.md index b1a3853824..76fec61b85 100644 --- a/docs/framework/lit/guide/virtualization.md +++ b/docs/framework/lit/guide/virtualization.md @@ -59,14 +59,13 @@ import { VirtualizerController } from '@tanstack/lit-virtual' const features = tableFeatures({ columnSizingFeature, rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, }) // in your LitElement's render method: const table = this.tableController.table({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data: this.data, }) diff --git a/docs/framework/lit/quick-start.md b/docs/framework/lit/quick-start.md index e79d34fa1f..bd1a340bb1 100644 --- a/docs/framework/lit/quick-start.md +++ b/docs/framework/lit/quick-start.md @@ -70,7 +70,6 @@ export class PersonTable extends LitElement { const table = this.tableController.table( { features, - rowModels: {}, // the core row model is included by default columns, data: this.data, }, @@ -123,7 +122,7 @@ export class PersonTable extends LitElement { A few things to note: - `tableFeatures({})` declares which optional features the table uses. Registering only what you need keeps bundles small and gives TypeScript accurate types for the table instance. -- `rowModels: {}` is fine for a basic table because the core row model is always included. Feature row models (sorting, filtering, pagination) are registered here when you need them. +- The core row model is always included automatically. Feature row models (sorting, filtering, pagination) are registered as slots directly on the `tableFeatures({...})` call when you need them. - `FlexRender` renders the `header`, `cell`, and `footer` definitions from your columns, whether they are plain values or Lit templates. It is also attached to the instance as `table.FlexRender` if you prefer not to import it. - The second argument to `tableController.table(...)` is a state selector. It controls what `table.state` contains; an empty selector is fine until you use feature state. @@ -131,7 +130,7 @@ See the full [Basic TableController example](./examples/basic-table-controller) ## Add a Feature: Sorting -Features are opt-in in v9. To make columns sortable, register `rowSortingFeature` in `tableFeatures`, register a sorted row model under `rowModels`, and wire the header click handler. +Features are opt-in in v9. To make columns sortable, register `rowSortingFeature` and the `sortedRowModel` factory in `tableFeatures`, then wire the header click handler. ```ts import { @@ -145,6 +144,8 @@ import { const features = tableFeatures({ rowSortingFeature, // enables sorting APIs and state + sortedRowModel: createSortedRowModel(), // client-side sorting + sortFns, }) @customElement('person-table') @@ -155,9 +156,6 @@ export class PersonTable extends LitElement { const table = this.tableController.table( { features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), // client-side sorting - }, columns, data: this.data, }, @@ -204,7 +202,7 @@ export class PersonTable extends LitElement { } ``` -Clicking a header now toggles between ascending, descending, and unsorted. Every other feature follows this same pattern: register the feature, register its row model if it has one, and use the APIs it adds to the table, columns, and rows. See the [Sorting Guide](./guide/sorting.md) and the [Sorting example](./examples/sorting) for custom sort functions, multi-sorting, and per-column options. +Clicking a header now toggles between ascending, descending, and unsorted. Every other feature follows this same pattern: register the feature (and its row model factory as a slot on `tableFeatures` if it has one), then use the APIs it adds to the table, columns, and rows. See the [Sorting Guide](./guide/sorting.md) and the [Sorting example](./examples/sorting) for custom sort functions, multi-sorting, and per-column options. ## Where to Go Next @@ -215,12 +213,13 @@ Clicking a header now toggles between ascending, descending, and unsorted. Every **Composable tables.** When multiple tables in your app share features, row models, and component conventions, define them once with `createTableHook`: ```ts -const features = tableFeatures({ rowSortingFeature }) - -const { useAppTable, createAppColumnHelper } = createTableHook({ - features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, }) + +const { useAppTable, createAppColumnHelper } = createTableHook({ features }) ``` Then call `useAppTable(this, { columns, data })` from your components instead of managing a `TableController` directly. See the [Composable Tables Guide](./guide/composable-tables.md) for the full pattern, including pre-bound cell and header components. diff --git a/docs/framework/lit/reference/classes/TableController.md b/docs/framework/lit/reference/classes/TableController.md index 3c858115ff..8d1cde26f5 100644 --- a/docs/framework/lit/reference/classes/TableController.md +++ b/docs/framework/lit/reference/classes/TableController.md @@ -5,7 +5,7 @@ title: TableController # Class: TableController\ -Defined in: [TableController.ts:139](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L139) +Defined in: [TableController.ts:138](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L138) A Lit ReactiveController for TanStack Table integration. @@ -24,7 +24,6 @@ class MyTable extends LitElement { const table = this.tableController.table( { features, - rowModels: {}, columns, data, }, @@ -57,7 +56,7 @@ class MyTable extends LitElement { new TableController(host): TableController; ``` -Defined in: [TableController.ts:150](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L150) +Defined in: [TableController.ts:149](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L149) #### Parameters @@ -77,7 +76,7 @@ Defined in: [TableController.ts:150](https://github.com/TanStack/table/blob/main host: ReactiveControllerHost; ``` -Defined in: [TableController.ts:143](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L143) +Defined in: [TableController.ts:142](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L142) ## Methods @@ -87,7 +86,7 @@ Defined in: [TableController.ts:143](https://github.com/TanStack/table/blob/main hostConnected(): void; ``` -Defined in: [TableController.ts:251](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L251) +Defined in: [TableController.ts:250](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L250) Called when the host is connected to the component tree. For custom element hosts, this corresponds to the `connectedCallback()` lifecycle, @@ -111,7 +110,7 @@ ReactiveController.hostConnected hostDisconnected(): void; ``` -Defined in: [TableController.ts:255](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L255) +Defined in: [TableController.ts:254](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L254) Called when the host is disconnected from the component tree. For custom element hosts, this corresponds to the `disconnectedCallback()` lifecycle, @@ -136,7 +135,7 @@ ReactiveController.hostDisconnected table(tableOptions, selector?): LitTable; ``` -Defined in: [TableController.ts:170](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L170) +Defined in: [TableController.ts:169](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L169) Returns the Lit-backed table instance for the current render pass. @@ -169,7 +168,7 @@ options into the same table instance and expose selected state through ```ts const table = this.tableController.table( - { features, rowModels: {}, columns, data }, + { features, columns, data }, (state) => ({ sorting: state.sorting }), ) ``` diff --git a/docs/framework/lit/reference/functions/createTableHook.md b/docs/framework/lit/reference/functions/createTableHook.md index 6b13f5576d..2799d874db 100644 --- a/docs/framework/lit/reference/functions/createTableHook.md +++ b/docs/framework/lit/reference/functions/createTableHook.md @@ -121,7 +121,7 @@ TFeatures is already known from the createTableHook call; TData is inferred from ##### tableOptions -`Omit`\<`TableOptions`\<`TFeatures`, `TData`\>, `"features"` \| `"rowModels"`\> +`Omit`\<`TableOptions`\<`TFeatures`, `TData`\>, `"features"`\> ##### selector? @@ -303,12 +303,12 @@ export const { rowPaginationFeature, rowSortingFeature, columnFilteringFeature, - }), - rowModels: { paginatedRowModel: createPaginatedRowModel(), - sortedRowModel: createSortedRowModel(sortFns), - filteredRowModel: createFilteredRowModel(filterFns), - }, + sortedRowModel: createSortedRowModel(), + filteredRowModel: createFilteredRowModel(), + sortFns, + filterFns, + }), tableComponents: { PaginationControls, RowCount }, cellComponents: { TextCell, NumberCell }, headerComponents: { SortIndicator, ColumnFilter }, diff --git a/docs/framework/preact/guide/column-faceting.md b/docs/framework/preact/guide/column-faceting.md index 4be4cbaa28..198902367b 100644 --- a/docs/framework/preact/guide/column-faceting.md +++ b/docs/framework/preact/guide/column-faceting.md @@ -13,16 +13,18 @@ Want to skip to the implementation? Check out these Preact examples: ```tsx import { useTable, tableFeatures, columnFacetingFeature, columnFilteringFeature, createFacetedRowModel, createFacetedUniqueValues, createFacetedMinMaxValues, createFilteredRowModel, filterFns } from '@tanstack/preact-table' -const features = tableFeatures({ columnFacetingFeature, columnFilteringFeature }) +const features = tableFeatures({ + columnFacetingFeature, + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + facetedRowModel: createFacetedRowModel(), + facetedUniqueValues: createFacetedUniqueValues(), + facetedMinMaxValues: createFacetedMinMaxValues(), + filterFns, +}) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - facetedRowModel: createFacetedRowModel(), - facetedUniqueValues: createFacetedUniqueValues(), - facetedMinMaxValues: createFacetedMinMaxValues(), - }, columns, data, }) @@ -34,7 +36,7 @@ Faceting is a feature that generates lists of values from your table's data, eit ### Column Faceting Row Models -In order to use any of the column faceting features, add the `columnFacetingFeature` to your features and the appropriate faceted row models to `rowModels`. Faceting exists to power filter UIs, so in practice you will also register the `columnFilteringFeature` and a `filteredRowModel`. Without a filtered row model, the faceted row models fall back to the pre-filtered rows and the facet values will not react to other columns' filters. +In order to use any of the column faceting features, add the `columnFacetingFeature` and the appropriate faceted row model factories to your `tableFeatures` call. Faceting exists to power filter UIs, so in practice you will also register the `columnFilteringFeature` and a `filteredRowModel`. Without a filtered row model, the faceted row models fall back to the pre-filtered rows and the facet values will not react to other columns' filters. ```ts import { @@ -49,16 +51,18 @@ import { filterFns, } from '@tanstack/preact-table' -const features = tableFeatures({ columnFacetingFeature, columnFilteringFeature }) +const features = tableFeatures({ + columnFacetingFeature, + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), // facet values react to other columns' filters + facetedRowModel: createFacetedRowModel(), // required for faceting (other faceted row models depend on this) + facetedMinMaxValues: createFacetedMinMaxValues(), // if you need min/max values + facetedUniqueValues: createFacetedUniqueValues(), // if you need a list of unique values + filterFns, +}) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), // facet values react to other columns' filters - facetedRowModel: createFacetedRowModel(), // required for faceting (other faceted row models depend on this) - facetedMinMaxValues: createFacetedMinMaxValues(), // if you need min/max values - facetedUniqueValues: createFacetedUniqueValues(), // if you need a list of unique values - }, columns, data, }) @@ -108,26 +112,28 @@ const [min, max] = table.getGlobalFacetedMinMaxValues() ?? [0, 1]; ### Custom (Server-Side) Faceting -Instead of using the built-in client-side faceting features, you can implement your own faceting logic on the server-side and pass the faceted values to the client-side. Supply custom `rowModels.facetedUniqueValues` and `rowModels.facetedMinMaxValues` factories. Each factory receives the table and a column ID and returns a thunk that resolves the faceted values. The column instance APIs (`column.getFacetedUniqueValues()` and `column.getFacetedMinMaxValues()`) will then return your server-provided values. +Instead of using the built-in client-side faceting features, you can implement your own faceting logic on the server-side and pass the faceted values to the client-side. Supply custom `facetedUniqueValues` and `facetedMinMaxValues` factory slots on `tableFeatures`. Each factory receives the table and a column ID and returns a thunk that resolves the faceted values. The column instance APIs (`column.getFacetedUniqueValues()` and `column.getFacetedMinMaxValues()`) will then return your server-provided values. ```ts const facetingQuery = useQuery( //... ) +const features = tableFeatures({ + columnFacetingFeature, + facetedUniqueValues: (_table, columnId) => () => { + const uniqueValueMap = new Map() + //... populate from facetingQuery data for this columnId + return uniqueValueMap + }, + facetedMinMaxValues: (_table, columnId) => () => { + //... read from facetingQuery data for this columnId + return [min, max] + }, +}) + const table = useTable({ features, - rowModels: { - facetedUniqueValues: (_table, columnId) => () => { - const uniqueValueMap = new Map() - //... populate from facetingQuery data for this columnId - return uniqueValueMap - }, - facetedMinMaxValues: (_table, columnId) => () => { - //... read from facetingQuery data for this columnId - return [min, max] - }, - }, columns, data, //... diff --git a/docs/framework/preact/guide/column-filtering.md b/docs/framework/preact/guide/column-filtering.md index 2f30d3aa67..b117419bb1 100644 --- a/docs/framework/preact/guide/column-filtering.md +++ b/docs/framework/preact/guide/column-filtering.md @@ -15,13 +15,14 @@ Want to skip to the implementation? Check out these Preact examples: ```tsx import { useTable, tableFeatures, columnFilteringFeature, createFilteredRowModel, filterFns } from '@tanstack/preact-table' -const features = tableFeatures({ columnFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, columns, data, }) @@ -55,14 +56,13 @@ If you're not sure, you can always start with client-side filtering and paginati If you have decided that you need to implement server-side filtering instead of using the built-in client-side filtering, here's how you do that. -No `filteredRowModel` is needed for manual server-side filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to `rowModels`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. +No `filteredRowModel` is needed for manual server-side filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to `tableFeatures`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. ```tsx -const features = tableFeatures({ columnFilteringFeature }) +const features = tableFeatures({ columnFilteringFeature }) // no filteredRowModel for manual server-side filtering const table = useTable({ features, - rowModels: {}, // no filteredRowModel needed for manual server-side filtering data, columns, manualFiltering: true, @@ -73,7 +73,7 @@ const table = useTable({ ### Client-Side Filtering -If you are using the built-in client-side filtering features, add the `columnFilteringFeature` to your features and the `filteredRowModel` to your row models. Import `createFilteredRowModel` and `filterFns` from TanStack Table: +If you are using the built-in client-side filtering features, add the `columnFilteringFeature`, the `filteredRowModel` factory, and `filterFns` to your `tableFeatures` call. Import `createFilteredRowModel` and `filterFns` from TanStack Table: ```tsx import { @@ -84,13 +84,14 @@ import { filterFns, } from '@tanstack/preact-table' -const features = tableFeatures({ columnFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, data, columns, }) @@ -119,7 +120,6 @@ For reactive reads that should re-render your UI, use `table.state.columnFilters ```tsx const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, //... @@ -145,7 +145,6 @@ const columnFilters = useSelector(columnFiltersAtom) const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, //... @@ -162,7 +161,6 @@ const [columnFilters, setColumnFilters] = useState([]) //... const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, //... @@ -180,7 +178,6 @@ If you do not need to control the column filter state in your own state manageme ```tsx const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, //... @@ -268,33 +265,28 @@ const columns = [ } ] //... +const myCustomFilterFn: FilterFn = (row, columnId, filterValue) => { + return // true or false based on your custom logic +} + +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns: { + ...filterFns, + myCustomFilterFn, + startsWith: startsWithFilterFn, // defined elsewhere + }, +}) + const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel({ - ...filterFns, - myCustomFilterFn: (row, columnId, filterValue) => { - return // true or false based on your custom logic - }, - startsWith: startsWithFilterFn, // defined elsewhere - }), - }, columns, data, }) ``` -> **TypeScript Note:** For `filterFn: 'myCustomFilterFn'` string references to typecheck, augment the `FilterFns` interface with a `declare module` block: -> -> ```tsx -> declare module '@tanstack/preact-table' { -> interface FilterFns { -> myCustomFilterFn: FilterFn -> } -> } -> ``` -> -> Alternatively, skip the registry and the augmentation entirely by passing the function directly to the `filterFn` column option. See the [Fuzzy Search example](../examples/filters-fuzzy) for a complete registration with module augmentation. +> **TypeScript Note:** For `filterFn: 'myCustomFilterFn'` string references to typecheck, register the function in the `filterFns` slot on `tableFeatures` (as shown above). TypeScript infers the registered names from the slot automatically. Alternatively, skip the registry entirely by passing the function directly to the `filterFn` column option. See the [Fuzzy Search example](../examples/filters-fuzzy) for a complete registration example. ##### Customize Filter Function Behavior @@ -346,7 +338,6 @@ const columns = [ //... const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, enableColumnFilters: false, // disable column filtering for all columns @@ -364,14 +355,16 @@ By default, filtering is done from parent rows down, so if a parent row is filte However, if you want to allow sub-rows to be filtered and searched through, regardless of whether the parent row is filtered out, you can set the `filterFromLeafRows` table option to `true`. Setting this option to `true` will cause filtering to be done from leaf rows up, which means parent rows will be included so long as one of their child or grand-child rows is also included. ```tsx -const features = tableFeatures({ columnFilteringFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnFilteringFeature, + rowExpandingFeature, + filteredRowModel: createFilteredRowModel(), + expandedRowModel: createExpandedRowModel(), + filterFns, +}) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - expandedRowModel: createExpandedRowModel(), - }, columns, data, filterFromLeafRows: true, // filter and search through sub-rows @@ -385,14 +378,16 @@ By default, filtering is done for all rows in a tree, no matter if they are root Use `maxLeafRowFilterDepth: 0` if you want to preserve a parent row's sub-rows from being filtered out while the parent row is passing the filter. ```tsx -const features = tableFeatures({ columnFilteringFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnFilteringFeature, + rowExpandingFeature, + filteredRowModel: createFilteredRowModel(), + expandedRowModel: createExpandedRowModel(), + filterFns, +}) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - expandedRowModel: createExpandedRowModel(), - }, columns, data, maxLeafRowFilterDepth: 0, // only filter root level parent rows out diff --git a/docs/framework/preact/guide/column-ordering.md b/docs/framework/preact/guide/column-ordering.md index edeb998a09..cd3b301ecf 100644 --- a/docs/framework/preact/guide/column-ordering.md +++ b/docs/framework/preact/guide/column-ordering.md @@ -16,7 +16,6 @@ const features = tableFeatures({ columnOrderingFeature }) const table = useTable({ features, - rowModels: {}, columns, data, }) @@ -49,7 +48,6 @@ const features = tableFeatures({ columnOrderingFeature }) const table = useTable({ features, - rowModels: {}, //... initialState: { columnOrder: ['columnId1', 'columnId2', 'columnId3'], @@ -83,7 +81,6 @@ const columnOrder = useSelector(columnOrderAtom) // subscribe wherever it is nee const table = useTable({ features, - rowModels: {}, //... atoms: { columnOrder: columnOrderAtom, @@ -101,7 +98,6 @@ const [columnOrder, setColumnOrder] = useState(['columnId1', ' //... const table = useTable({ features, - rowModels: {}, //... state: { columnOrder, diff --git a/docs/framework/preact/guide/column-pinning.md b/docs/framework/preact/guide/column-pinning.md index 55c383c0d7..f180bd9a98 100644 --- a/docs/framework/preact/guide/column-pinning.md +++ b/docs/framework/preact/guide/column-pinning.md @@ -18,7 +18,6 @@ const features = tableFeatures({ columnPinningFeature }) const table = useTable({ features, - rowModels: {}, columns, data, }) @@ -60,7 +59,6 @@ const columnPinning = useSelector(columnPinningAtom) // subscribe wherever it is const table = useTable({ features, - rowModels: {}, //... atoms: { columnPinning: columnPinningAtom, @@ -79,7 +77,6 @@ const [columnPinning, setColumnPinning] = useState({ const table = useTable({ features, - rowModels: {}, //... state: { columnPinning, @@ -97,7 +94,6 @@ A very common use case is to pin some columns by default. You can do this by eit ```tsx const table = useTable({ features, - rowModels: {}, //... initialState: { columnPinning: { diff --git a/docs/framework/preact/guide/column-resizing.md b/docs/framework/preact/guide/column-resizing.md index 2069293644..2ff178658f 100644 --- a/docs/framework/preact/guide/column-resizing.md +++ b/docs/framework/preact/guide/column-resizing.md @@ -17,7 +17,6 @@ const features = tableFeatures({ columnResizingFeature }) const table = useTable({ features, - rowModels: {}, columns, data, }) @@ -57,7 +56,6 @@ const columns = [ const table = useTable({ features, - rowModels: {}, columns, data, }) @@ -174,7 +172,6 @@ const columnResizing = useSelector(columnResizingAtom) // subscribe wherever it const table = useTable({ features, - rowModels: {}, columns, data, atoms: { @@ -197,7 +194,6 @@ const [columnResizing, setColumnResizing] = useState({ const table = useTable({ features, - rowModels: {}, columns, data, state: { diff --git a/docs/framework/preact/guide/column-sizing.md b/docs/framework/preact/guide/column-sizing.md index f8a2b5511d..5584d522e8 100644 --- a/docs/framework/preact/guide/column-sizing.md +++ b/docs/framework/preact/guide/column-sizing.md @@ -16,7 +16,6 @@ const features = tableFeatures({ columnSizingFeature }) const table = useTable({ features, - rowModels: {}, columns, data, }) @@ -55,7 +54,6 @@ const columns = [ const table = useTable({ features, - rowModels: {}, defaultColumn: { size: 200, // starting column size minSize: 50, // enforced during column resizing @@ -137,7 +135,6 @@ const columnSizing = useSelector(columnSizingAtom) // subscribe wherever it is n const table = useTable({ features, - rowModels: {}, columns, data, atoms: { @@ -155,7 +152,6 @@ const [columnSizing, setColumnSizing] = useState({}) const table = useTable({ features, - rowModels: {}, columns, data, state: { diff --git a/docs/framework/preact/guide/column-visibility.md b/docs/framework/preact/guide/column-visibility.md index e34f8404a7..165bbe8026 100644 --- a/docs/framework/preact/guide/column-visibility.md +++ b/docs/framework/preact/guide/column-visibility.md @@ -16,7 +16,6 @@ const features = tableFeatures({ columnVisibilityFeature }) const table = useTable({ features, - rowModels: {}, columns, data, }) @@ -49,7 +48,6 @@ const columnVisibility = useSelector(columnVisibilityAtom) // subscribe wherever const table = useTable({ features, - rowModels: {}, //... atoms: { columnVisibility: columnVisibilityAtom, @@ -70,7 +68,6 @@ const [columnVisibility, setColumnVisibility] = useState( const table = useTable({ features, - rowModels: {}, //... state: { columnVisibility, @@ -89,7 +86,6 @@ const features = tableFeatures({ columnVisibilityFeature }) const table = useTable({ features, - rowModels: {}, //... initialState: { columnVisibility: { diff --git a/docs/framework/preact/guide/composable-tables.md b/docs/framework/preact/guide/composable-tables.md index dc17ebac8c..5aab1684bb 100644 --- a/docs/framework/preact/guide/composable-tables.md +++ b/docs/framework/preact/guide/composable-tables.md @@ -54,6 +54,11 @@ const features = tableFeatures({ columnFilteringFeature, rowPaginationFeature, rowSortingFeature, + sortedRowModel: createSortedRowModel(), + filteredRowModel: createFilteredRowModel(), + paginatedRowModel: createPaginatedRowModel(), + sortFns, + filterFns, }) export const { @@ -64,11 +69,6 @@ export const { useHeaderContext, } = createTableHook({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - filteredRowModel: createFilteredRowModel(filterFns), - paginatedRowModel: createPaginatedRowModel(), - }, getRowId: (row) => row.id, tableComponents: { PaginationControls, @@ -137,7 +137,7 @@ Registered cell components use `useCellContext()` internally, and registered hea ## Table Rendering -Create each table with `useAppTable`. You pass table-specific options like `key`, `columns`, and `data`; the shared `features`, `rowModels`, `getRowId`, and component registries come from the hook. +Create each table with `useAppTable`. You pass table-specific options like `key`, `columns`, and `data`; the shared `features` (which includes row model factories), `getRowId`, and component registries come from the hook. ```tsx const table = useAppTable( diff --git a/docs/framework/preact/guide/custom-features.md b/docs/framework/preact/guide/custom-features.md index 4f1b17d056..727fe9cf55 100644 --- a/docs/framework/preact/guide/custom-features.md +++ b/docs/framework/preact/guide/custom-features.md @@ -268,7 +268,6 @@ const features = tableFeatures({ densityPlugin }) const table = useTable({ features, - rowModels: {}, columns, data, //.. @@ -286,7 +285,6 @@ const [density, setDensity] = useState('md') const table = useTable({ features, - rowModels: {}, columns, data, //... diff --git a/docs/framework/preact/guide/expanding.md b/docs/framework/preact/guide/expanding.md index 180c2a6e92..f56a54a2e4 100644 --- a/docs/framework/preact/guide/expanding.md +++ b/docs/framework/preact/guide/expanding.md @@ -13,13 +13,13 @@ Want to skip to the implementation? Check out these Preact examples: ```tsx import { useTable, tableFeatures, rowExpandingFeature, createExpandedRowModel } from '@tanstack/preact-table' -const features = tableFeatures({ rowExpandingFeature }) +const features = tableFeatures({ + rowExpandingFeature, + expandedRowModel: createExpandedRowModel(), +}) const table = useTable({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, columns, data, }) @@ -38,7 +38,7 @@ There are multiple use cases for expanding features in TanStack Table that will ### Enable Client-Side Expanding -To use the client-side expanding features, add the `rowExpandingFeature` to your features and the `expandedRowModel` to your row models: +To use the client-side expanding features, add the `rowExpandingFeature` and the `expandedRowModel` factory to your `tableFeatures` call: ```ts import { @@ -48,13 +48,13 @@ import { createExpandedRowModel, } from '@tanstack/preact-table' -const features = tableFeatures({ rowExpandingFeature }) +const features = tableFeatures({ + rowExpandingFeature, + expandedRowModel: createExpandedRowModel(), +}) const table = useTable({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, // other options... }) ``` @@ -99,9 +99,6 @@ Then you can use the getSubRows function to return the children array in each ro ```ts const table = useTable({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, getSubRows: (row) => row.children, // return the children array as sub-rows // other options... }) @@ -121,9 +118,6 @@ import { Fragment } from 'preact' //... const table = useTable({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, getRowCanExpand: (row) => true, // Add your logic to determine if a row can be expanded. True means all rows include expanded data // other options... }) @@ -168,7 +162,6 @@ const expanded = useSelector(expandedAtom) const table = useTable({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, // other options... atoms: { expanded: expandedAtom, // expanding APIs now update expandedAtom @@ -183,7 +176,6 @@ const [expanded, setExpanded] = useState({}) const table = useTable({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, // other options... state: { expanded, @@ -261,15 +253,17 @@ Use `table.setExpanded` to update the expanded state directly. `table.resetExpan By default, the filtering process starts from the parent rows and moves downwards. This means if a parent row is excluded by the filter, all its child rows will also be excluded. However, you can change this behavior by using the `filterFromLeafRows` option. When this option is enabled, the filtering process starts from the leaf (child) rows and moves upwards. This ensures that a parent row will be included in the filtered results as long as at least one of its child or grandchild rows meets the filter criteria. Additionally, you can control how deep into the child hierarchy the filter process goes by using the `maxLeafRowFilterDepth` option. This option allows you to specify the maximum depth of child rows that the filter should consider. ```ts -const features = tableFeatures({ columnFilteringFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnFilteringFeature, + rowExpandingFeature, + filteredRowModel: createFilteredRowModel(), + expandedRowModel: createExpandedRowModel(), + filterFns, +}) //... const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - expandedRowModel: createExpandedRowModel(), - }, getSubRows: (row) => row.subRows, filterFromLeafRows: true, // search through the expanded rows maxLeafRowFilterDepth: 1, // limit the depth of the expanded rows that are searched @@ -284,7 +278,6 @@ By default, expanded rows are paginated along with the rest of the table (which ```ts const table = useTable({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, // other options... paginateExpandedRows: false, }) @@ -307,7 +300,6 @@ A common reason to set `autoResetExpanded: false` is editing data while viewing ```ts const table = useTable({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, // other options... autoResetExpanded: false, // keep expanded state when data changes // autoResetAll: false, // or turn off all auto resets at once @@ -319,11 +311,10 @@ const table = useTable({ If you are doing server-side expansion, you can enable manual row expansion by setting the manualExpanding option to true. This means that the `getExpandedRowModel` will not be used to expand rows and you would be expected to perform the expansion in your own data model. ```ts -const features = tableFeatures({ rowExpandingFeature }) +const features = tableFeatures({ rowExpandingFeature }) // no expandedRowModel for manual expanding const table = useTable({ features, - rowModels: {}, // no expandedRowModel needed for manual expanding // other options... manualExpanding: true, }) diff --git a/docs/framework/preact/guide/fuzzy-filtering.md b/docs/framework/preact/guide/fuzzy-filtering.md index aa7129d076..3fc267586c 100644 --- a/docs/framework/preact/guide/fuzzy-filtering.md +++ b/docs/framework/preact/guide/fuzzy-filtering.md @@ -11,20 +11,24 @@ Want to skip to the implementation? Check out these Preact examples: ### Preact Setup ```tsx -import { useTable, tableFeatures, columnFilteringFeature, globalFilteringFeature, rowSortingFeature, createFilteredRowModel, createSortedRowModel, filterFns, sortFns } from '@tanstack/preact-table' +import { useTable, tableFeatures, columnFilteringFeature, globalFilteringFeature, rowSortingFeature, createFilteredRowModel, createSortedRowModel, filterFns, sortFns, metaHelper } from '@tanstack/preact-table' +import type { RankingInfo } from '@tanstack/match-sorter-utils' + +interface FuzzyFilterMeta { itemRank?: RankingInfo } const features = tableFeatures({ columnFilteringFeature, globalFilteringFeature, rowSortingFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + sortFns: { ...sortFns, fuzzy: fuzzySort }, + filterMeta: metaHelper(), }) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data, }) @@ -50,9 +54,15 @@ Here's an example of a custom fuzzy filter function: ```typescript import { rankItem } from '@tanstack/match-sorter-utils' import type { RankingInfo } from '@tanstack/match-sorter-utils' -import type { FilterFn, RowData } from '@tanstack/preact-table' +import type { FilterFn, TableFeatures, RowData, metaHelper } from '@tanstack/preact-table' + +// Define the shape of the filter meta stored by the fuzzy filter +interface FuzzyFilterMeta { itemRank?: RankingInfo } + +// Extend TableFeatures so the FilterFn type has access to the filterMeta shape +type FuzzyFeatures = TableFeatures & { filterMeta: FuzzyFilterMeta } -const fuzzyFilter: FilterFn = ( +const fuzzyFilter: FilterFn = ( row, columnId, value, @@ -71,23 +81,28 @@ const fuzzyFilter: FilterFn = ( In this function, we're using the `rankItem` function from the `@tanstack/match-sorter-utils` library to rank the item. We then store the ranking information in the filter meta of the row (the `addMeta` callback is optional, so call it with optional chaining), and return whether the item passed the ranking criteria. -To reference this filter function by the string name `'fuzzy'` (and to type the stored filter meta), augment the `FilterFns` and `FilterMeta` interfaces with a `declare module` block: +Instead of using `declare module` augmentation, register the fuzzy filter and its meta shape as slots on `tableFeatures`: ```typescript -declare module '@tanstack/preact-table' { - // add the fuzzy filter to the filterFns registry types - interface FilterFns { - fuzzy: FilterFn - } - interface FilterMeta { - itemRank?: RankingInfo - } -} +import { metaHelper } from '@tanstack/preact-table' + +const features = tableFeatures({ + columnFilteringFeature, + globalFilteringFeature, + rowSortingFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + sortFns: { ...sortFns, fuzzy: fuzzySort }, + filterMeta: metaHelper(), +}) ``` +The `filterMeta` slot types the metadata stored by `addMeta`. The `filterFns` slot registers the function so it can be referenced by the string name `'fuzzy'` in column definitions and `globalFilterFn`. + ### Using Fuzzy Filtering with Global Filtering -To use fuzzy filtering with global filtering, register the fuzzy filter function in the registry passed to `createFilteredRowModel` and reference it in the `globalFilterFn` option of the table: +To use fuzzy filtering with global filtering, register the fuzzy filter function in the `filterFns` slot on `tableFeatures` and reference it in the `globalFilterFn` option of the table: ```typescript import { @@ -100,23 +115,22 @@ import { createSortedRowModel, filterFns, sortFns, + metaHelper, } from '@tanstack/preact-table' const features = tableFeatures({ columnFilteringFeature, globalFilteringFeature, rowSortingFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), // needed if you want sorting with fuzzy rank + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + sortFns, + filterMeta: metaHelper(), }) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel({ - ...filterFns, - fuzzy: fuzzyFilter, - }), - sortedRowModel: createSortedRowModel(sortFns), // needed if you want sorting with fuzzy rank - }, columns, data, globalFilterFn: 'fuzzy', @@ -151,7 +165,7 @@ import { compareItems } from '@tanstack/match-sorter-utils' import { sortFns } from '@tanstack/preact-table' import type { SortFn } from '@tanstack/preact-table' -const fuzzySort: SortFn = (rowA, rowB, columnId) => { +const fuzzySort: SortFn = (rowA, rowB, columnId) => { let dir = 0 // Only sort by rank if the column has ranking information @@ -182,4 +196,4 @@ You can then pass this sorting function directly to the `sortFn` option of the c } ``` -> **Note:** Unlike `filterFn: 'fuzzy'` above, `fuzzySort` is passed as a function rather than a string. A string reference like `sortFn: 'fuzzySort'` would only work if you also registered the function in the registry passed to `createSortedRowModel` (e.g. `createSortedRowModel({ ...sortFns, fuzzySort })`) and augmented the `SortFns` interface the same way as `FilterFns`. Passing the function directly skips both steps. +> **Note:** Unlike `filterFn: 'fuzzy'` above, `fuzzySort` is passed as a function rather than a string. A string reference like `sortFn: 'fuzzySort'` would require you to also add it to the `sortFns` slot on `tableFeatures` (e.g. `sortFns: { ...sortFns, fuzzySort }`). Passing the function directly skips registration. diff --git a/docs/framework/preact/guide/global-filtering.md b/docs/framework/preact/guide/global-filtering.md index ede1b0ed55..4d3415f473 100644 --- a/docs/framework/preact/guide/global-filtering.md +++ b/docs/framework/preact/guide/global-filtering.md @@ -12,15 +12,17 @@ Want to skip to the implementation? Check out these Preact examples: ### Preact Setup ```tsx -import { useTable, tableFeatures, globalFilteringFeature, createFilteredRowModel, filterFns } from '@tanstack/preact-table' +import { useTable, tableFeatures, columnFilteringFeature, globalFilteringFeature, createFilteredRowModel, filterFns } from '@tanstack/preact-table' -const features = tableFeatures({ globalFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + globalFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, columns, data, }) @@ -52,20 +54,20 @@ If you're not sure, you can always start with client-side filtering and paginati If you have decided that you need to implement server-side global filtering instead of using the built-in client-side global filtering, here's how you do that. -No `filteredRowModel` is needed for manual server-side global filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to `rowModels`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. +No `filteredRowModel` is needed for manual server-side global filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to `tableFeatures`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. ```tsx import { useTable, tableFeatures, + columnFilteringFeature, globalFilteringFeature, } from '@tanstack/preact-table' -const features = tableFeatures({ globalFilteringFeature }) +const features = tableFeatures({ columnFilteringFeature, globalFilteringFeature }) // no filteredRowModel for manual server-side global filtering const table = useTable({ features, - rowModels: {}, // no filteredRowModel needed for manual server-side global filtering data, columns, manualFiltering: true, @@ -76,24 +78,27 @@ Note: When using manual global filtering, many of the options that are discussed ### Client-Side Global Filtering -If you are using the built-in client-side global filtering, add the `globalFilteringFeature` to your features and the `filteredRowModel` to your row models: +If you are using the built-in client-side global filtering, add the `globalFilteringFeature` (along with its required `columnFilteringFeature` prerequisite), the `filteredRowModel` factory, and `filterFns` to your `tableFeatures` call: ```tsx import { useTable, tableFeatures, + columnFilteringFeature, globalFilteringFeature, createFilteredRowModel, filterFns, } from '@tanstack/preact-table' -const features = tableFeatures({ globalFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + globalFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, // other options... }) ``` @@ -105,9 +110,6 @@ The `globalFilterFn` option allows you to specify the filter function that will ```tsx const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, data, columns, globalFilterFn: 'includesString', // built-in filter function @@ -147,7 +149,6 @@ const globalFilter = useSelector(globalFilterAtom) const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... atoms: { globalFilter: globalFilterAtom, // table.setGlobalFilter now updates globalFilterAtom @@ -162,7 +163,6 @@ const [globalFilter, setGlobalFilter] = useState('') const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... state: { globalFilter, @@ -204,7 +204,6 @@ const customFilterFn = (row, columnId, filterValue) => { const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... globalFilterFn: customFilterFn, }) @@ -217,7 +216,6 @@ If you want to set an initial global filter state when the table is initialized, ```tsx const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... initialState: { globalFilter: 'search term', // if not controlling globalFilter state, set initial state here @@ -245,7 +243,6 @@ const columns = [ //... const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... columns, enableGlobalFilter: false, // disable global filtering for all columns diff --git a/docs/framework/preact/guide/grouping.md b/docs/framework/preact/guide/grouping.md index 0b21b05127..085912727f 100644 --- a/docs/framework/preact/guide/grouping.md +++ b/docs/framework/preact/guide/grouping.md @@ -13,13 +13,14 @@ Want to skip to the implementation? Check out these Preact examples: ```tsx import { useTable, tableFeatures, columnGroupingFeature, createGroupedRowModel, aggregationFns } from '@tanstack/preact-table' -const features = tableFeatures({ columnGroupingFeature }) +const features = tableFeatures({ + columnGroupingFeature, + groupedRowModel: createGroupedRowModel(), + aggregationFns, +}) const table = useTable({ features, - rowModels: { - groupedRowModel: createGroupedRowModel(aggregationFns), - }, columns, data, }) @@ -35,7 +36,7 @@ Grouping can also affect column order. There are 3 table features that can reord 2. Manual [Column Ordering](./column-ordering) - A manually specified column order is applied. 3. **Grouping** - If grouping is enabled, a grouping state is active, and `tableOptions.groupedColumnMode` is set to `'reorder' | 'remove'`, then the grouped columns are reordered to the start of the column flow. -To use the grouping feature, add the `columnGroupingFeature` to your features and the `groupedRowModel` to your row models. The grouped row model is responsible for grouping the rows based on the grouping state. +To use the grouping feature, add the `columnGroupingFeature`, the `groupedRowModel` factory, and `aggregationFns` to your `tableFeatures` call. The grouped row model is responsible for grouping the rows based on the grouping state. ```tsx import { @@ -46,13 +47,14 @@ import { aggregationFns, } from '@tanstack/preact-table' -const features = tableFeatures({ columnGroupingFeature }) +const features = tableFeatures({ + columnGroupingFeature, + groupedRowModel: createGroupedRowModel(), + aggregationFns, +}) const table = useTable({ features, - rowModels: { - groupedRowModel: createGroupedRowModel(aggregationFns), - }, // other options... }) ``` @@ -61,14 +63,16 @@ When grouping state is active, the table will add matching rows as subRows to th To allow the user to expand and collapse the grouped rows, you can use the expanding feature. ```tsx -const features = tableFeatures({ columnGroupingFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnGroupingFeature, + rowExpandingFeature, + groupedRowModel: createGroupedRowModel(), + expandedRowModel: createExpandedRowModel(), + aggregationFns, +}) const table = useTable({ features, - rowModels: { - groupedRowModel: createGroupedRowModel(aggregationFns), - expandedRowModel: createExpandedRowModel(), - }, // other options... }) ``` @@ -92,7 +96,6 @@ By default, when a column is grouped, it is moved to the start of the table. You ```tsx const table = useTable({ features, - rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) }, // other options... groupedColumnMode: 'reorder', }) @@ -128,16 +131,21 @@ There are several built-in aggregation functions that you can use: You can define custom aggregation functions in the registry that you pass to `createGroupedRowModel`. The registry is a record where the keys are the names of the aggregation functions, and the values are the aggregation functions themselves. You can then reference these aggregation functions by name in a column's `aggregationFn` option. ```tsx +const myCustomAggregation: AggregationFn = (columnId, leafRows, childRows) => { + // return the aggregated value +} + +const features = tableFeatures({ + columnGroupingFeature, + groupedRowModel: createGroupedRowModel(), + aggregationFns: { + ...aggregationFns, + myCustomAggregation, + }, +}) + const table = useTable({ features, - rowModels: { - groupedRowModel: createGroupedRowModel({ - ...aggregationFns, - myCustomAggregation: (columnId, leafRows, childRows) => { - // return the aggregated value - }, - }), - }, // other options... }) ``` @@ -150,28 +158,17 @@ const column = columnHelper.accessor('key', { }) ``` -> **TypeScript Note:** For `aggregationFn: 'myCustomAggregation'` string references to typecheck, augment the `AggregationFns` interface with a `declare module` block: -> -> ```tsx -> declare module '@tanstack/preact-table' { -> interface AggregationFns { -> myCustomAggregation: AggregationFn -> } -> } -> ``` -> -> Alternatively, skip the registry and the augmentation entirely by passing the function directly to the `aggregationFn` column option. +> **TypeScript Note:** For `aggregationFn: 'myCustomAggregation'` string references to typecheck, register the function in the `aggregationFns` slot on `tableFeatures` (as shown above). TypeScript infers the registered names from the slot automatically. Alternatively, skip the registry entirely by passing the function directly to the `aggregationFn` column option. ### Manual Grouping If you are doing server-side grouping and aggregation, you can enable manual grouping using the manualGrouping option. When this option is set to true, the table will not automatically group rows using getGroupedRowModel() and instead will expect you to manually group the rows before passing them to the table. ```tsx -const features = tableFeatures({ columnGroupingFeature }) +const features = tableFeatures({ columnGroupingFeature }) // no groupedRowModel for manual grouping const table = useTable({ features, - rowModels: {}, // no groupedRowModel needed for manual grouping // other options... manualGrouping: true, }) @@ -194,7 +191,6 @@ const grouping = useSelector(groupingAtom) const table = useTable({ features, - rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) }, // other options... atoms: { grouping: groupingAtom, // grouping APIs now update groupingAtom @@ -209,7 +205,6 @@ const [grouping, setGrouping] = useState([]) const table = useTable({ features, - rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) }, // other options... state: { grouping, diff --git a/docs/framework/preact/guide/migrating.md b/docs/framework/preact/guide/migrating.md index 33005354a9..8521065011 100644 --- a/docs/framework/preact/guide/migrating.md +++ b/docs/framework/preact/guide/migrating.md @@ -2,15 +2,19 @@ title: Migrating to TanStack Table v9 (Preact) --- +> [!NOTE] +> `v9.0.0-beta.10` introduces a breaking change in how row models are defined in order to bring increased type-safety features. Row model factories and function registries now live as slots on the `features` object instead of a separate `rowModels` option, and the factories no longer take arguments. If you migrated on an earlier beta, see the [Row Model Factories](#row-model-factories) section below for the new shape. + ## What's New in TanStack Table v9 TanStack Table v9 is a major release with a smaller, more explicit table setup. The core table logic is familiar, but the table instance now declares exactly which features, row models, and state subscriptions it needs. -### 1. Tree-shaking +### 1. Tree Shaking and Extensibility - **Features are tree-shakeable**: features are registered explicitly. If a table only needs sorting and pagination, it does not need to ship filtering, grouping, or row selection code. -- **Row models are registered explicitly**: row model factories now live under `rowModels` instead of root `get*RowModel` options. -- **Function registries moved to factories**: row model factories like `createSortedRowModel(sortFns)`, `createFilteredRowModel(filterFns)`, and `createGroupedRowModel(aggregationFns)` receive the function registry they need. This lets unused built-in functions tree-shake. +- **Row models are registered explicitly**: row model factories now live as slots on `tableFeatures({...})` instead of root `get*RowModel` options or a `rowModels` object. +- **Function registries moved to features slots**: `sortFns`, `filterFns`, and `aggregationFns` are registered as slots on `tableFeatures` alongside the row model factories. This lets unused built-in functions tree-shake. +- **Custom feature plugins with full type safety**: The same plugin architecture that powers the built-in features is open to your own code. Write a custom feature with its own state, options, and APIs, register it in `tableFeatures()` alongside the built-ins, and the table's types pick it all up automatically. See the [Custom Features Guide](./custom-features.md). ### 2. State Management @@ -24,13 +28,19 @@ TanStack Table v9 is a major release with a smaller, more explicit table setup. - **`tableOptions()`**: build type-safe partial table option objects that can be shared and composed. - **`createTableHook()`**: create app-level Preact table factories with shared features, row models, defaults, and component conventions. +### 4. Improved Type Safety (No More Declaration Merging) + +- **Function registries replace `declare module` augmentation**: Custom filter, sort, and aggregation functions are registered by name in the `filterFns` / `sortFns` / `aggregationFns` slots on `tableFeatures()`. The registered keys become the valid, type-safe string values for `filterFn`, `sortFn`, `globalFilterFn`, and `aggregationFn` in your column definitions, with full inference. No more augmenting the `FilterFns` / `SortFns` / `AggregationFns` interfaces globally. +- **Per-table meta slots**: The type-only `tableMeta`, `columnMeta`, and `filterMeta` slots declare meta types for a single table instead of merging into a global interface. The `filterMeta` slot types both the `addMeta` callback in filter functions and the values read back from `row.columnFiltersMeta`. +- **Feature-gated APIs and validated prerequisites**: APIs like `table.setSorting` only exist on the table type when their feature is registered, and `tableFeatures()` validates slot prerequisites at the type level. Registering `sortFns` without `rowSortingFeature`, or `globalFilteringFeature` without `columnFilteringFeature`, is a typed error instead of a silent runtime no-op. + ### The Good News: Most Upgrades Are Opt-in - You can start with `stockFeatures` while migrating, then replace it with explicit feature registration. - `useTable` defaults to v8-style full state subscriptions. Pass a narrower selector only when you want to optimize re-renders. - Table markup is largely unchanged. Rows, headers, cells, and feature APIs still drive rendering. -The main migration is changing from the React adapter used through `preact/compat` to the native Preact adapter: `useReactTable` becomes `useTable`, and `get*RowModel` options become `features` plus `rowModels`. +The main migration is changing from the React adapter used through `preact/compat` to the native Preact adapter: `useReactTable` becomes `useTable`, and `get*RowModel` options become feature and row model factory slots on `tableFeatures`. ## Preact v8 Context @@ -56,9 +66,9 @@ import { useTable } from '@tanstack/preact-table' const table = useTable(options) ``` -### New Required Options: `features` and `rowModels` +### New Required Options: `features` -In v9, a table must declare its feature set and row models. +In v9, a table must declare its feature set. Row model factories are registered as slots on `tableFeatures` rather than as a separate `rowModels` option. ```tsx // v8 / before: React adapter through preact/compat @@ -73,17 +83,16 @@ const table = useReactTable({ // v9 import { tableFeatures, useTable } from '@tanstack/preact-table' -const features = tableFeatures({}) +const features = tableFeatures({}) // core row model is automatic const table = useTable({ features, - rowModels: {}, // core row model is automatic columns, data, }) ``` -Keep `features` and reusable `rowModels` objects outside the component when possible so references stay stable. +Keep the `features` object outside the component when possible so the reference stays stable. --- @@ -121,7 +130,6 @@ import { stockFeatures, useTable } from '@tanstack/preact-table' const table = useTable({ features: stockFeatures, - rowModels, columns, data, }) @@ -148,20 +156,20 @@ const table = useTable({ --- -## The `rowModels` Option +## Row Model Factories -Row models process data for features like filtering, sorting, grouping, expanding, faceting, and pagination. In v9, they are configured under `rowModels`. +Row models process data for features like filtering, sorting, grouping, expanding, faceting, and pagination. In v9, row model factories and function registries are slots on `tableFeatures` rather than a separate `rowModels` option. ### Migration Mapping -| v8 Option | v9 `rowModels` Key | v9 Factory Function | +| v8 Option | v9 `tableFeatures` Slot | v9 Factory | |---|---|---| | `getCoreRowModel()` | (automatic) | Not needed | -| `getFilteredRowModel()` | `filteredRowModel` | `createFilteredRowModel(filterFns)` | -| `getSortedRowModel()` | `sortedRowModel` | `createSortedRowModel(sortFns)` | +| `getFilteredRowModel()` + `filterFns` | `filteredRowModel` + `filterFns` | `createFilteredRowModel()` | +| `getSortedRowModel()` + `sortingFns` | `sortedRowModel` + `sortFns` | `createSortedRowModel()` | | `getPaginationRowModel()` | `paginatedRowModel` | `createPaginatedRowModel()` | | `getExpandedRowModel()` | `expandedRowModel` | `createExpandedRowModel()` | -| `getGroupedRowModel()` | `groupedRowModel` | `createGroupedRowModel(aggregationFns)` | +| `getGroupedRowModel()` + `aggregationFns` | `groupedRowModel` + `aggregationFns` | `createGroupedRowModel()` | | `getFacetedRowModel()` | `facetedRowModel` | `createFacetedRowModel()` | | `getFacetedMinMaxValues()` | `facetedMinMaxValues` | `createFacetedMinMaxValues()` | | `getFacetedUniqueValues()` | `facetedUniqueValues` | `createFacetedUniqueValues()` | @@ -209,17 +217,15 @@ const features = tableFeatures({ columnFilteringFeature, rowPaginationFeature, rowSortingFeature, -}) - -const rowModels = { - filteredRowModel: createFilteredRowModel(filterFns), - sortedRowModel: createSortedRowModel(sortFns), + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), paginatedRowModel: createPaginatedRowModel(), -} + filterFns, + sortFns, +}) const table = useTable({ features, - rowModels, columns, data, }) @@ -259,7 +265,6 @@ By default, `table.state` is reactive and contains the full registered table sta ```tsx const table = useTable({ features, - rowModels, columns, data, }) @@ -273,7 +278,6 @@ Pass a custom selector when you want `table.state` to contain only the reactive const table = useTable( { features, - rowModels, columns, data, }, @@ -291,7 +295,7 @@ Passing `(state) => state` is equivalent to the default selector and is no longe For large tables, opt the parent out and subscribe lower in the tree: ```tsx -const table = useTable(options, () => null) +const table = useTable({ features, columns, data }, () => null) ``` ### Optimized Rendering with `table.Subscribe` @@ -336,7 +340,6 @@ const [pagination, setPagination] = useState({ const table = useTable({ features, - rowModels, columns, data, state: { @@ -370,7 +373,6 @@ function MyTable({ columns, data }) { const table = useTable({ features, - rowModels, columns, data, atoms: { @@ -448,7 +450,6 @@ import { tableOptions } from '@tanstack/preact-table' const baseOptions = tableOptions({ features, - rowModels, defaultColumn: { minSize: 40, }, @@ -472,10 +473,7 @@ Use it when several tables share feature registration, row models, defaults, or ```tsx import { createTableHook } from '@tanstack/preact-table' -const { useAppTable, createAppColumnHelper } = createTableHook({ - features, - rowModels, -}) +const { useAppTable, createAppColumnHelper } = createTableHook({ features }) const columnHelper = createAppColumnHelper() @@ -600,6 +598,39 @@ const features = tableFeatures({ See the new [Table and Column Meta Guide](../../../guide/table-and-column-meta) for full details on both approaches. +### `FilterFns`/`SortFns`/`AggregationFns`/`FilterMeta` Augmentation Replaced by Registry Slots + +In v8, making a custom function usable as a string reference (like `filterFn: 'fuzzy'`) required `declare module` augmentation of the `FilterFns` interface, and typing filter meta required augmenting `FilterMeta`. In v9, registering the function in the matching registry slot does both jobs with no global augmentation: + +```tsx +// v8 / before: React adapter through preact/compat +declare module '@tanstack/react-table' { + interface FilterFns { + fuzzy: FilterFn + } + interface FilterMeta { + itemRank: RankingInfo + } +} + +// v9 - register in the slot; the key becomes a valid string value +interface FuzzyFilterMeta { + itemRank?: RankingInfo +} + +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + filterMeta: metaHelper(), +}) + +// 'fuzzy' now typechecks in column defs for tables using these features +columnHelper.accessor('name', { filterFn: 'fuzzy' }) +``` + +The same pattern applies to `sortFns` (for `sortFn` string values) and `aggregationFns` (for `aggregationFn` string values). See the [Fuzzy Filtering Guide](./fuzzy-filtering.md) for a complete example. + ### `RowData` Type Restriction `RowData` is now constrained to record-like objects or arrays. Prefer object row types such as: @@ -619,10 +650,11 @@ type Person = { - [ ] Replace `@tanstack/react-table` imports used through `preact/compat` with `@tanstack/preact-table`. - [ ] Replace `useReactTable` with `useTable`. - [ ] Define `features` using `tableFeatures()` (or use `stockFeatures`). -- [ ] Replace root `get*RowModel` options with `rowModels`. +- [ ] Migrate `get*RowModel()` options (or earlier-beta `rowModels: {...}` entries) to `tableFeatures({...})` slots (e.g. `filteredRowModel: createFilteredRowModel()`). - [ ] Drop `getCoreRowModel`; the core row model is automatic. -- [ ] Move `sortingFns`, `filterFns`, and `aggregationFns` into row model factories. +- [ ] Move `sortingFns`, `filterFns`, and `aggregationFns` into `tableFeatures` slots (not factory args). - [ ] Rename `sortingFn` to `sortFn` and `sortingFns` to `sortFns`. +- [ ] Replace `declare module` augmentation for `FilterFns`/`SortFns`/`AggregationFns`/`FilterMeta` with registry slots on `tableFeatures` (`filterFns`, `sortFns`, `aggregationFns`, `filterMeta`). - [ ] Update column helpers and types to include `typeof features`. - [ ] Replace broad `table.getState()` reads with `table.state`, `table.store.state`, or `table.atoms..get()`. - [ ] Replace `onStateChange` with per-slice `on[State]Change` or external atoms. diff --git a/docs/framework/preact/guide/pagination.md b/docs/framework/preact/guide/pagination.md index 934faec427..7c52fb0ced 100644 --- a/docs/framework/preact/guide/pagination.md +++ b/docs/framework/preact/guide/pagination.md @@ -13,13 +13,13 @@ Want to skip to the implementation? Check out these Preact examples: ```tsx import { useTable, tableFeatures, rowPaginationFeature, createPaginatedRowModel } from '@tanstack/preact-table' -const features = tableFeatures({ rowPaginationFeature }) +const features = tableFeatures({ + rowPaginationFeature, + paginatedRowModel: createPaginatedRowModel(), +}) const table = useTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, }) @@ -53,7 +53,7 @@ Alternatively, instead of paginating the data, you can render all rows of a larg #### Pagination Row Model -If you want to take advantage of the built-in client-side pagination in TanStack Table, add the `rowPaginationFeature` to your features and the `paginatedRowModel` to your row models: +If you want to take advantage of the built-in client-side pagination in TanStack Table, add the `rowPaginationFeature` and the `paginatedRowModel` factory to your `tableFeatures` call: ```tsx import { @@ -63,13 +63,13 @@ import { createPaginatedRowModel, } from '@tanstack/preact-table' -const features = tableFeatures({ rowPaginationFeature }) +const features = tableFeatures({ + rowPaginationFeature, + paginatedRowModel: createPaginatedRowModel(), +}) const table = useTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, }) @@ -92,11 +92,10 @@ import { rowPaginationFeature, } from '@tanstack/preact-table' -const features = tableFeatures({ rowPaginationFeature }) +const features = tableFeatures({ rowPaginationFeature }) // no paginatedRowModel for server-side pagination const table = useTable({ features, - rowModels: {}, // no paginatedRowModel needed for server-side pagination columns, data, manualPagination: true, // turn off client-side pagination @@ -130,7 +129,10 @@ import { type PaginationState, } from '@tanstack/preact-table' -const features = tableFeatures({ rowPaginationFeature }) +const features = tableFeatures({ + rowPaginationFeature, + paginatedRowModel: createPaginatedRowModel(), +}) const paginationAtom = useCreateAtom({ pageIndex: 0, // initial page index @@ -142,9 +144,6 @@ const pagination = useSelector(paginationAtom) const table = useTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, atoms: { @@ -163,9 +162,6 @@ const [pagination, setPagination] = useState({ const table = useTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, onPaginationChange: setPagination, @@ -180,9 +176,6 @@ Alternatively, if you have no need for managing the `pagination` state in your o ```tsx const table = useTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, initialState: { @@ -207,9 +200,6 @@ By default, `pageIndex` is reset to `0` whenever the client-side row models reco ```tsx const table = useTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, autoResetPageIndex: false, // turn off auto reset of pageIndex diff --git a/docs/framework/preact/guide/row-pinning.md b/docs/framework/preact/guide/row-pinning.md index abf3624a2b..3b56031c36 100644 --- a/docs/framework/preact/guide/row-pinning.md +++ b/docs/framework/preact/guide/row-pinning.md @@ -16,7 +16,6 @@ const features = tableFeatures({ rowPinningFeature }) const table = useTable({ features, - rowModels: {}, columns, data, }) @@ -33,7 +32,7 @@ There are 2 table features that can reorder rows, which happen in the following ### Enable Row Pinning -To use row pinning, add `rowPinningFeature` to your features. Row pinning does not require a row model factory, so `rowModels` can stay empty unless your table uses other row-model features. +To use row pinning, add `rowPinningFeature` to your features. Row pinning does not require a row model factory; add other row model factory slots to `tableFeatures` only if your table uses them. ```tsx import { @@ -46,7 +45,6 @@ const features = tableFeatures({ rowPinningFeature }) const table = useTable({ features, - rowModels: {}, columns, data, }) @@ -68,7 +66,6 @@ You can pin rows by default with `initialState.rowPinning`: ```tsx const table = useTable({ features, - rowModels: {}, columns, data, initialState: { @@ -95,7 +92,6 @@ const rowPinning = useSelector(rowPinningAtom) // subscribe wherever it is neede const table = useTable({ features, - rowModels: {}, columns, data, atoms: { @@ -114,7 +110,6 @@ const [rowPinning, setRowPinning] = useState({ const table = useTable({ features, - rowModels: {}, columns, data, state: { @@ -219,7 +214,6 @@ By default, all rows can be pinned. You can disable row pinning for the whole ta ```tsx const table = useTable({ features, - rowModels: {}, columns, data, enableRowPinning: row => row.original.status !== 'archived', @@ -235,7 +229,6 @@ Set `keepPinnedRows` to `false` if pinned rows should only render when they are ```tsx const table = useTable({ features, - rowModels: {}, columns, data, keepPinnedRows: false, diff --git a/docs/framework/preact/guide/row-selection.md b/docs/framework/preact/guide/row-selection.md index 9eeb198b35..daf51470f9 100644 --- a/docs/framework/preact/guide/row-selection.md +++ b/docs/framework/preact/guide/row-selection.md @@ -17,7 +17,6 @@ const features = tableFeatures({ rowSelectionFeature }) const table = useTable({ features, - rowModels: {}, columns, data, }) @@ -64,7 +63,6 @@ const rowSelection = useSelector(rowSelectionAtom) const table = useTable({ features, - rowModels: {}, //... atoms: { rowSelection: rowSelectionAtom, // selection APIs now update rowSelectionAtom @@ -79,7 +77,6 @@ const [rowSelection, setRowSelection] = useState({}) const table = useTable({ features, - rowModels: {}, //... onRowSelectionChange: setRowSelection, state: { @@ -95,7 +92,6 @@ By default, the row id for each row is simply the `row.index`. If you are using ```ts const table = useTable({ features, - rowModels: {}, //... getRowId: (row) => row.uuid, // use the row's uuid from your database as the row id }) diff --git a/docs/framework/preact/guide/sorting.md b/docs/framework/preact/guide/sorting.md index c5543f9e04..a118384371 100644 --- a/docs/framework/preact/guide/sorting.md +++ b/docs/framework/preact/guide/sorting.md @@ -13,13 +13,14 @@ Want to skip to the implementation? Check out these Preact examples: ```tsx import { useTable, tableFeatures, rowSortingFeature, createSortedRowModel, sortFns } from '@tanstack/preact-table' -const features = tableFeatures({ rowSortingFeature }) +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, +}) const table = useTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data, }) @@ -50,7 +51,6 @@ For reactive reads that should re-render your UI, use `table.state.sorting` (sel ```tsx const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, //... @@ -76,7 +76,6 @@ const sorting = useSelector(sortingAtom) const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, //... @@ -93,7 +92,6 @@ const [sorting, setSorting] = useState([]) //... const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, //... @@ -111,7 +109,6 @@ If you do not need to control the sorting state in your own state management or ```tsx const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, //... @@ -139,7 +136,7 @@ If you plan to just use your own server-side sorting in your back-end logic, you ```tsx import { useCreateAtom, useSelector } from '@tanstack/preact-store' -const features = tableFeatures({ rowSortingFeature }) // feature needed for sorting state/APIs +const features = tableFeatures({ rowSortingFeature }) // feature needed for sorting state/APIs; no sortedRowModel for manual sorting const sortingAtom = useCreateAtom([]) @@ -148,7 +145,6 @@ const sorting = useSelector(sortingAtom) //... const table = useTable({ features, - rowModels: {}, // no sortedRowModel needed for manual sorting columns, data, manualSorting: true, // use pre-sorted row model instead of sorted row model @@ -164,7 +160,7 @@ Hoisting the sorting state into your own scope (with an external atom or the `st ### Client-Side Sorting -To implement client-side sorting, add the `rowSortingFeature` to your features and the `sortedRowModel` to your row models. Import `createSortedRowModel` and `sortFns` from TanStack Table: +To implement client-side sorting, add the `rowSortingFeature` and the `sortedRowModel` factory to your `tableFeatures` call. Import `createSortedRowModel` and `sortFns` from TanStack Table: ```tsx import { @@ -175,13 +171,14 @@ import { sortFns, } from '@tanstack/preact-table' -const features = tableFeatures({ rowSortingFeature }) +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, +}) const table = useTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data, }) @@ -252,35 +249,30 @@ const columns = [ } ] //... +const myCustomSortFn: SortFn = (rowA, rowB, columnId) => + rowA.original[columnId] > rowB.original[columnId] + ? 1 + : rowA.original[columnId] < rowB.original[columnId] + ? -1 + : 0 + +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns: { + ...sortFns, + myCustomSortFn, + }, +}) + const table = useTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel({ - ...sortFns, - myCustomSortFn: (rowA, rowB, columnId) => - rowA.original[columnId] > rowB.original[columnId] - ? 1 - : rowA.original[columnId] < rowB.original[columnId] - ? -1 - : 0, - }), - }, columns, data, }) ``` -> **TypeScript Note:** For `sortFn: 'myCustomSortFn'` string references to typecheck, augment the `SortFns` interface with a `declare module` block: -> -> ```tsx -> declare module '@tanstack/preact-table' { -> interface SortFns { -> myCustomSortFn: SortFn -> } -> } -> ``` -> -> Alternatively, skip the registry and the augmentation entirely by passing the function directly to the `sortFn` column option. +> **TypeScript Note:** For `sortFn: 'myCustomSortFn'` string references to typecheck, register the function in the `sortFns` slot on `tableFeatures` (as shown above). TypeScript will infer the registered names from the slot automatically. Alternatively, skip the registry entirely by passing the function directly to the `sortFn` column option. ### Customize Sorting @@ -306,7 +298,6 @@ const columns = [ //... const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, enableSorting: false, // disable sorting for the entire table @@ -334,7 +325,6 @@ const columns = [ //... const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, sortDescFirst: true, //sort by all columns in descending order first (default is ascending for string columns and descending for number columns) @@ -401,7 +391,6 @@ Once a column is sorted and `enableSortingRemoval` is `false`, toggling the sort ```tsx const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, enableSortingRemoval: false, // disable the ability to remove sorting on columns (sorting can never return to 'none' once applied) @@ -428,7 +417,6 @@ const columns = [ //... const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, enableMultiSort: false, // disable multi-sorting for the entire table @@ -442,7 +430,6 @@ By default, the `Shift` key is used to trigger multi-sorting. You can change thi ```tsx const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, isMultiSortEvent: (e) => true, // normal click triggers multi-sorting @@ -458,7 +445,6 @@ By default, there is no limit to the number of columns that can be sorted at onc ```tsx const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, maxMultiSortColCount: 3, // only allow 3 columns to be sorted at once @@ -472,7 +458,6 @@ By default, the ability to remove multi-sorts is enabled. You can disable this b ```tsx const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, enableMultiRemove: false, // disable the ability to remove multi-sorts diff --git a/docs/framework/preact/guide/table-state.md b/docs/framework/preact/guide/table-state.md index 500258d72b..17d6795aa3 100644 --- a/docs/framework/preact/guide/table-state.md +++ b/docs/framework/preact/guide/table-state.md @@ -47,14 +47,13 @@ For example, if `features` includes `rowPaginationFeature`, TypeScript exposes p const features = tableFeatures({ rowPaginationFeature, rowSortingFeature, + paginatedRowModel: createPaginatedRowModel(), + sortedRowModel: createSortedRowModel(), + sortFns, }) const table = useTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data, }) @@ -104,9 +103,6 @@ You can pass your own selector to make `table.state` contain only the reactive s const table = useTable( { features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, }, @@ -124,7 +120,6 @@ For large tables, you can also opt the parent table component out of table-state const table = useTable( { features, - rowModels: {}, columns, data, }, @@ -144,10 +139,6 @@ Without a `source` prop, `table.Subscribe` subscribes to `table.store` and requi const table = useTable( { features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, }, @@ -229,10 +220,6 @@ If you only need to customize the starting value for some table state, use `init ```tsx const table = useTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, initialState: { @@ -285,6 +272,7 @@ import { const features = tableFeatures({ rowPaginationFeature, + // no paginatedRowModel: manual server-side pagination does not need it }) function App() { @@ -302,7 +290,6 @@ function App() { const table = useTable({ features, - rowModels: {}, columns, data: dataQuery.data?.rows ?? [], rowCount: dataQuery.data?.rowCount, @@ -333,10 +320,6 @@ const [pagination, setPagination] = useState({ const table = useTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, state: { @@ -361,9 +344,6 @@ const [sorting, setSorting] = useState([]) const table = useTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data, state: { @@ -383,9 +363,6 @@ const [pagination, setPagination] = useState({ const table = useTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, state: { diff --git a/docs/framework/preact/guide/virtualization.md b/docs/framework/preact/guide/virtualization.md index 84b4692a27..d7963b1d61 100644 --- a/docs/framework/preact/guide/virtualization.md +++ b/docs/framework/preact/guide/virtualization.md @@ -59,13 +59,12 @@ import { useVirtualizer } from '@tanstack/react-virtual' // requires the preact/ const features = tableFeatures({ columnSizingFeature, rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, }) const table = useTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data, }) diff --git a/docs/framework/preact/quick-start.md b/docs/framework/preact/quick-start.md index 772da71740..b735064206 100644 --- a/docs/framework/preact/quick-start.md +++ b/docs/framework/preact/quick-start.md @@ -61,7 +61,6 @@ export function PersonTable() { const table = useTable({ key: 'person-table', // registers this table with the devtools features, - rowModels: {}, // the core row model is included by default columns, data, }) @@ -101,7 +100,7 @@ export function PersonTable() { A few things to note: - `tableFeatures({})` declares which optional features the table uses. Registering only what you need keeps bundles small and gives TypeScript accurate types for the table instance. -- `rowModels: {}` is fine for a basic table because the core row model is always included. Feature row models (sorting, filtering, pagination) are registered here when you need them. +- The core row model is always included automatically. Feature row models (sorting, filtering, pagination) are registered as slots directly on the `tableFeatures({...})` call when you need them. - `table.FlexRender` renders the `header`, `cell`, and `footer` definitions from your columns, whether they are plain values or Preact components. - The `key` option is optional unless you use the [TanStack Table Devtools](../../devtools). The devtools identify tables by `key`, and you register a table by calling `useTanStackTableDevtools(table)` from `@tanstack/preact-table-devtools`. @@ -109,7 +108,7 @@ See the full [Basic useTable example](./examples/basic-use-table) for a runnable ## Add a Feature: Sorting -Features are opt-in in v9. To make columns sortable, register `rowSortingFeature` in `tableFeatures`, register a sorted row model under `rowModels`, and wire the header click handler. +Features are opt-in in v9. To make columns sortable, register `rowSortingFeature` and the sorted row model in `tableFeatures`, then wire the header click handler. ```tsx import { @@ -122,15 +121,14 @@ import { const features = tableFeatures({ rowSortingFeature, // enables sorting APIs and state + sortedRowModel: createSortedRowModel(), // client-side sorting + sortFns, // built-in sort functions }) export function PersonTable() { const table = useTable({ key: 'person-table', features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), // client-side sorting - }, columns, data, }) @@ -167,7 +165,7 @@ export function PersonTable() { } ``` -Clicking a header now toggles between ascending, descending, and unsorted. Every other feature follows this same pattern: register the feature, register its row model if it has one, and use the APIs it adds to the table, columns, and rows. See the [Sorting Guide](./guide/sorting.md) and the [Sorting example](./examples/sorting) for custom sort functions, multi-sorting, and per-column options. +Clicking a header now toggles between ascending, descending, and unsorted. Every other feature follows this same pattern: register the feature and its row model factory in `tableFeatures`, then use the APIs those features add to the table, columns, and rows. See the [Sorting Guide](./guide/sorting.md) and the [Sorting example](./examples/sorting) for custom sort functions, multi-sorting, and per-column options. ## Where to Go Next @@ -178,12 +176,13 @@ Clicking a header now toggles between ascending, descending, and unsorted. Every **Composable tables.** When multiple tables in your app share features, row models, and component conventions, define them once with `createTableHook`: ```tsx -const features = tableFeatures({ rowSortingFeature }) - -const { useAppTable, createAppColumnHelper } = createTableHook({ - features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, }) + +const { useAppTable, createAppColumnHelper } = createTableHook({ features }) ``` See the [Composable Tables Guide](./guide/composable-tables.md) for the full pattern, including pre-bound cell and header components. diff --git a/docs/framework/preact/reference/functions/createTableHook.md b/docs/framework/preact/reference/functions/createTableHook.md index fe67a43c27..9f9106de2c 100644 --- a/docs/framework/preact/reference/functions/createTableHook.md +++ b/docs/framework/preact/reference/functions/createTableHook.md @@ -116,7 +116,7 @@ TFeatures is already known from the createTableHook call; TData is inferred from ##### tableOptions -`Omit`\<`TableOptions`\<`TFeatures`, `TData`\>, `"features"` \| `"rowModels"`\> +`Omit`\<`TableOptions`\<`TFeatures`, `TData`\>, `"features"`\> ##### selector? @@ -256,12 +256,12 @@ export const { rowPaginationFeature, rowSortingFeature, columnFilteringFeature, - }), - rowModels: { paginatedRowModel: createPaginatedRowModel(), - sortedRowModel: createSortedRowModel(sortFns), - filteredRowModel: createFilteredRowModel(filterFns), - }, + sortedRowModel: createSortedRowModel(), + filteredRowModel: createFilteredRowModel(), + sortFns, + filterFns, + }), tableComponents: { PaginationControls, RowCount }, cellComponents: { TextCell, NumberCell }, headerComponents: { SortIndicator, ColumnFilter }, diff --git a/docs/framework/preact/reference/functions/useTable.md b/docs/framework/preact/reference/functions/useTable.md index 931b40504e..8189a123d8 100644 --- a/docs/framework/preact/reference/functions/useTable.md +++ b/docs/framework/preact/reference/functions/useTable.md @@ -9,7 +9,7 @@ title: useTable function useTable(tableOptions, selector?): PreactTable; ``` -Defined in: [useTable.ts:113](https://github.com/TanStack/table/blob/main/packages/preact-table/src/useTable.ts#L113) +Defined in: [useTable.ts:112](https://github.com/TanStack/table/blob/main/packages/preact-table/src/useTable.ts#L112) Creates a Preact table instance backed by TanStack Store atoms. @@ -53,7 +53,6 @@ subscriptions. const table = useTable( { features, - rowModels: {}, columns, data, }, diff --git a/docs/framework/react/guide/column-faceting.md b/docs/framework/react/guide/column-faceting.md index bd439a5a4d..685f9b7422 100644 --- a/docs/framework/react/guide/column-faceting.md +++ b/docs/framework/react/guide/column-faceting.md @@ -13,16 +13,18 @@ Want to skip to the implementation? Check out these React examples: ```tsx import { useTable, tableFeatures, columnFacetingFeature, columnFilteringFeature, createFacetedRowModel, createFacetedUniqueValues, createFacetedMinMaxValues, createFilteredRowModel, filterFns } from '@tanstack/react-table' -const features = tableFeatures({ columnFacetingFeature, columnFilteringFeature }) +const features = tableFeatures({ + columnFacetingFeature, + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + facetedRowModel: createFacetedRowModel(), + facetedUniqueValues: createFacetedUniqueValues(), + facetedMinMaxValues: createFacetedMinMaxValues(), + filterFns, +}) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - facetedRowModel: createFacetedRowModel(), - facetedUniqueValues: createFacetedUniqueValues(), - facetedMinMaxValues: createFacetedMinMaxValues(), - }, columns, data, }) @@ -34,7 +36,7 @@ Faceting is a feature that generates lists of values from your table's data, eit ### Column Faceting Row Models -In order to use any of the column faceting features, add the `columnFacetingFeature` to your features and the appropriate faceted row models to `rowModels`. Faceting exists to power filter UIs, so in practice you will also register the `columnFilteringFeature` and a `filteredRowModel`. Without a filtered row model, the faceted row models fall back to the pre-filtered rows and the facet values will not react to other columns' filters. +In order to use any of the column faceting features, add the `columnFacetingFeature` and the appropriate faceted row model factories to your features. Faceting exists to power filter UIs, so in practice you will also register the `columnFilteringFeature` and a `filteredRowModel`. Without a filtered row model, the faceted row models fall back to the pre-filtered rows and the facet values will not react to other columns' filters. ```ts import { @@ -49,16 +51,18 @@ import { filterFns, } from '@tanstack/react-table' -const features = tableFeatures({ columnFacetingFeature, columnFilteringFeature }) +const features = tableFeatures({ + columnFacetingFeature, + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), // facet values react to other columns' filters + facetedRowModel: createFacetedRowModel(), // required for faceting (other faceted row models depend on this) + facetedMinMaxValues: createFacetedMinMaxValues(), // if you need min/max values + facetedUniqueValues: createFacetedUniqueValues(), // if you need a list of unique values + filterFns, +}) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), // facet values react to other columns' filters - facetedRowModel: createFacetedRowModel(), // required for faceting (other faceted row models depend on this) - facetedMinMaxValues: createFacetedMinMaxValues(), // if you need min/max values - facetedUniqueValues: createFacetedUniqueValues(), // if you need a list of unique values - }, columns, data, }) @@ -108,39 +112,44 @@ const [min, max] = table.getGlobalFacetedMinMaxValues() ?? [0, 1]; ### Custom (Server-Side) Faceting -Instead of using the built-in client-side faceting features, you can implement your own faceting logic on the server-side and pass the faceted values to the client-side. Supply custom `rowModels.facetedUniqueValues` and `rowModels.facetedMinMaxValues` factories. Each factory receives the table and a column ID and returns a thunk that resolves the faceted values. The column instance APIs (`column.getFacetedUniqueValues()` and `column.getFacetedMinMaxValues()`) will then return your server-provided values. +Instead of using the built-in client-side faceting features, you can implement your own faceting logic on the server-side and pass the faceted values to the client-side. Supply custom `facetedUniqueValues` and `facetedMinMaxValues` factories in the features object. Each factory receives the table and a column ID and returns a thunk that resolves the faceted values. The column instance APIs (`column.getFacetedUniqueValues()` and `column.getFacetedMinMaxValues()`) will then return your server-provided values. ```ts const facetingQuery = useQuery( //... ) +const features = tableFeatures({ + columnFacetingFeature, + facetedUniqueValues: (_table, columnId) => () => { + const uniqueValueMap = new Map() + //... populate from facetingQuery data for this columnId + return uniqueValueMap + }, + facetedMinMaxValues: (_table, columnId) => () => { + //... read from facetingQuery data for this columnId + return [min, max] + }, +}) + const table = useTable({ features, - rowModels: { - facetedUniqueValues: (_table, columnId) => () => { - const uniqueValueMap = new Map() - //... populate from facetingQuery data for this columnId - return uniqueValueMap - }, - facetedMinMaxValues: (_table, columnId) => () => { - //... read from facetingQuery data for this columnId - return [min, max] - }, - }, columns, data, //... }) ``` -The same factories also serve global faceting. Global faceting requests values with the internal `__global__` column ID, so you can branch on it inside the same `facetedUniqueValues` and `facetedMinMaxValues` factories to return table-wide facet values: +The same factories also serve global faceting. Global faceting requests values with the internal `__global__` column ID, so you can branch on it inside the same `facetedUniqueValues` and `facetedMinMaxValues` factories on the features object to return table-wide facet values: ```ts -facetedUniqueValues: (_table, columnId) => () => { - if (columnId !== '__global__') return new Map() // per-column facets - return new Map(globalFacets.uniqueValues) // global facets -}, +const features = tableFeatures({ + columnFacetingFeature, + facetedUniqueValues: (_table, columnId) => () => { + if (columnId !== '__global__') return new Map() // per-column facets + return new Map(globalFacets.uniqueValues) // global facets + }, +}) ``` Alternatively, you don't have to put any of your faceting logic through the TanStack Table APIs at all. Just fetch your lists and pass them to your filter components directly. diff --git a/docs/framework/react/guide/column-filtering.md b/docs/framework/react/guide/column-filtering.md index 7efc582881..63929b1a44 100644 --- a/docs/framework/react/guide/column-filtering.md +++ b/docs/framework/react/guide/column-filtering.md @@ -15,13 +15,14 @@ Want to skip to the implementation? Check out these React examples: ```tsx import { useTable, tableFeatures, columnFilteringFeature, createFilteredRowModel, filterFns } from '@tanstack/react-table' -const features = tableFeatures({ columnFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, columns, data, }) @@ -55,14 +56,13 @@ If you're not sure, you can always start with client-side filtering and paginati If you have decided that you need to implement server-side filtering instead of using the built-in client-side filtering, here's how you do that. -No `filteredRowModel` is needed for manual server-side filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to `rowModels`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. +No `filteredRowModel` is needed for manual server-side filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to the features object, you can tell the table to skip it by setting the `manualFiltering` option to `true`. ```tsx const features = tableFeatures({ columnFilteringFeature }) const table = useTable({ features, - rowModels: {}, // no filteredRowModel needed for manual server-side filtering data, columns, manualFiltering: true, @@ -73,7 +73,7 @@ const table = useTable({ ### Client-Side Filtering -If you are using the built-in client-side filtering features, add the `columnFilteringFeature` to your features and the `filteredRowModel` to your row models. Import `createFilteredRowModel` and `filterFns` from TanStack Table: +If you are using the built-in client-side filtering features, add the `columnFilteringFeature` and the `filteredRowModel` factory to your features. Import `createFilteredRowModel` and `filterFns` from TanStack Table: ```tsx import { @@ -84,13 +84,14 @@ import { filterFns, } from '@tanstack/react-table' -const features = tableFeatures({ columnFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, data, columns, }) @@ -119,7 +120,6 @@ For reactive reads that should re-render your UI, use `table.state.columnFilters ```tsx const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, //... @@ -145,7 +145,6 @@ const columnFilters = useSelector(columnFiltersAtom) const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, //... @@ -162,7 +161,6 @@ const [columnFilters, setColumnFilters] = useState([]) //... const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, //... @@ -180,7 +178,6 @@ If you do not need to control the column filter state in your own state manageme ```tsx const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, //... @@ -242,6 +239,20 @@ Every filter function receives: and should return `true` if the row should be included in the filtered rows, and `false` if it should be removed. ```tsx +const myCustomFilterFn: FilterFn = (row, columnId, filterValue) => { + return // true or false based on your custom logic +} + +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns: { + ...filterFns, + myCustomFilterFn, + startsWith: startsWithFilterFn, // defined elsewhere + }, +}) + const columns = [ { header: () => 'Name', @@ -256,7 +267,7 @@ const columns = [ { header: () => 'Birthday', accessorKey: 'birthday', - filterFn: 'myCustomFilterFn', // reference a custom filter function registered with createFilteredRowModel + filterFn: 'myCustomFilterFn', // reference a custom filter function registered in features }, { header: () => 'Profile', @@ -270,31 +281,12 @@ const columns = [ //... const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel({ - ...filterFns, - myCustomFilterFn: (row, columnId, filterValue) => { - return // true or false based on your custom logic - }, - startsWith: startsWithFilterFn, // defined elsewhere - }), - }, columns, data, }) ``` -> **TypeScript Note:** For `filterFn: 'myCustomFilterFn'` string references to typecheck, augment the `FilterFns` interface with a `declare module` block: -> -> ```tsx -> declare module '@tanstack/react-table' { -> interface FilterFns { -> myCustomFilterFn: FilterFn -> } -> } -> ``` -> -> Alternatively, skip the registry and the augmentation entirely by passing the function directly to the `filterFn` column option. See the [Fuzzy Search example](../examples/filters-fuzzy) for a complete registration with module augmentation. +> **TypeScript Note:** For `filterFn: 'myCustomFilterFn'` string references to typecheck, register the function in the `filterFns` slot on `tableFeatures` (as shown above). Alternatively, skip the registry entirely by passing the function directly to the `filterFn` column option. See the [Fuzzy Search example](../examples/filters-fuzzy) for a complete registration example. ##### Customize Filter Function Behavior @@ -346,7 +338,6 @@ const columns = [ //... const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, enableColumnFilters: false, // disable column filtering for all columns @@ -364,14 +355,16 @@ By default, filtering is done from parent rows down, so if a parent row is filte However, if you want to allow sub-rows to be filtered and searched through, regardless of whether the parent row is filtered out, you can set the `filterFromLeafRows` table option to `true`. Setting this option to `true` will cause filtering to be done from leaf rows up, which means parent rows will be included so long as one of their child or grand-child rows is also included. ```tsx -const features = tableFeatures({ columnFilteringFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnFilteringFeature, + rowExpandingFeature, + filteredRowModel: createFilteredRowModel(), + expandedRowModel: createExpandedRowModel(), + filterFns, +}) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - expandedRowModel: createExpandedRowModel(), - }, columns, data, filterFromLeafRows: true, // filter and search through sub-rows @@ -385,14 +378,16 @@ By default, filtering is done for all rows in a tree, no matter if they are root Use `maxLeafRowFilterDepth: 0` if you want to preserve a parent row's sub-rows from being filtered out while the parent row is passing the filter. ```tsx -const features = tableFeatures({ columnFilteringFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnFilteringFeature, + rowExpandingFeature, + filteredRowModel: createFilteredRowModel(), + expandedRowModel: createExpandedRowModel(), + filterFns, +}) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - expandedRowModel: createExpandedRowModel(), - }, columns, data, maxLeafRowFilterDepth: 0, // only filter root level parent rows out diff --git a/docs/framework/react/guide/column-ordering.md b/docs/framework/react/guide/column-ordering.md index f1067dc5e0..ffa6e91468 100644 --- a/docs/framework/react/guide/column-ordering.md +++ b/docs/framework/react/guide/column-ordering.md @@ -17,7 +17,6 @@ const features = tableFeatures({ columnOrderingFeature }) const table = useTable({ features, - rowModels: {}, columns, data, }) @@ -50,7 +49,6 @@ const features = tableFeatures({ columnOrderingFeature }) const table = useTable({ features, - rowModels: {}, //... initialState: { columnOrder: ['columnId1', 'columnId2', 'columnId3'], @@ -84,7 +82,6 @@ const columnOrder = useSelector(columnOrderAtom) // subscribe wherever it is nee const table = useTable({ features, - rowModels: {}, //... atoms: { columnOrder: columnOrderAtom, @@ -102,7 +99,6 @@ const [columnOrder, setColumnOrder] = useState(['columnId1', ' //... const table = useTable({ features, - rowModels: {}, //... state: { columnOrder, diff --git a/docs/framework/react/guide/column-pinning.md b/docs/framework/react/guide/column-pinning.md index f797a60a64..67b8e692b4 100644 --- a/docs/framework/react/guide/column-pinning.md +++ b/docs/framework/react/guide/column-pinning.md @@ -18,7 +18,6 @@ const features = tableFeatures({ columnPinningFeature }) const table = useTable({ features, - rowModels: {}, columns, data, }) @@ -60,7 +59,6 @@ const columnPinning = useSelector(columnPinningAtom) // subscribe wherever it is const table = useTable({ features, - rowModels: {}, //... atoms: { columnPinning: columnPinningAtom, @@ -79,7 +77,6 @@ const [columnPinning, setColumnPinning] = useState({ const table = useTable({ features, - rowModels: {}, //... state: { columnPinning, @@ -97,7 +94,6 @@ A very common use case is to pin some columns by default. You can do this by eit ```tsx const table = useTable({ features, - rowModels: {}, //... initialState: { columnPinning: { diff --git a/docs/framework/react/guide/column-resizing.md b/docs/framework/react/guide/column-resizing.md index a4d6b7ba55..7b05db750b 100644 --- a/docs/framework/react/guide/column-resizing.md +++ b/docs/framework/react/guide/column-resizing.md @@ -17,7 +17,6 @@ const features = tableFeatures({ columnResizingFeature }) const table = useTable({ features, - rowModels: {}, columns, data, }) @@ -57,7 +56,6 @@ const columns = [ const table = useTable({ features, - rowModels: {}, columns, data, }) @@ -174,7 +172,6 @@ const columnResizing = useSelector(columnResizingAtom) // subscribe wherever it const table = useTable({ features, - rowModels: {}, columns, data, atoms: { @@ -197,7 +194,6 @@ const [columnResizing, setColumnResizing] = useState({ const table = useTable({ features, - rowModels: {}, columns, data, state: { diff --git a/docs/framework/react/guide/column-sizing.md b/docs/framework/react/guide/column-sizing.md index de43f673d0..ff4d29c13e 100644 --- a/docs/framework/react/guide/column-sizing.md +++ b/docs/framework/react/guide/column-sizing.md @@ -16,7 +16,6 @@ const features = tableFeatures({ columnSizingFeature }) const table = useTable({ features, - rowModels: {}, columns, data, }) @@ -55,7 +54,6 @@ const columns = [ const table = useTable({ features, - rowModels: {}, defaultColumn: { size: 200, // starting column size minSize: 50, // enforced during column resizing @@ -137,7 +135,6 @@ const columnSizing = useSelector(columnSizingAtom) // subscribe wherever it is n const table = useTable({ features, - rowModels: {}, columns, data, atoms: { @@ -155,7 +152,6 @@ const [columnSizing, setColumnSizing] = useState({}) const table = useTable({ features, - rowModels: {}, columns, data, state: { diff --git a/docs/framework/react/guide/column-visibility.md b/docs/framework/react/guide/column-visibility.md index 230903d8d4..f27dab516f 100644 --- a/docs/framework/react/guide/column-visibility.md +++ b/docs/framework/react/guide/column-visibility.md @@ -16,7 +16,6 @@ const features = tableFeatures({ columnVisibilityFeature }) const table = useTable({ features, - rowModels: {}, columns, data, }) @@ -49,7 +48,6 @@ const columnVisibility = useSelector(columnVisibilityAtom) // subscribe wherever const table = useTable({ features, - rowModels: {}, //... atoms: { columnVisibility: columnVisibilityAtom, @@ -70,7 +68,6 @@ const [columnVisibility, setColumnVisibility] = useState( const table = useTable({ features, - rowModels: {}, //... state: { columnVisibility, @@ -89,7 +86,6 @@ const features = tableFeatures({ columnVisibilityFeature }) const table = useTable({ features, - rowModels: {}, //... initialState: { columnVisibility: { diff --git a/docs/framework/react/guide/composable-tables.md b/docs/framework/react/guide/composable-tables.md index 459e90a325..0930c7a7bd 100644 --- a/docs/framework/react/guide/composable-tables.md +++ b/docs/framework/react/guide/composable-tables.md @@ -54,6 +54,11 @@ const features = tableFeatures({ columnFilteringFeature, rowPaginationFeature, rowSortingFeature, + sortedRowModel: createSortedRowModel(), + filteredRowModel: createFilteredRowModel(), + paginatedRowModel: createPaginatedRowModel(), + sortFns, + filterFns, }) export const { @@ -64,11 +69,6 @@ export const { useHeaderContext, } = createTableHook({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - filteredRowModel: createFilteredRowModel(filterFns), - paginatedRowModel: createPaginatedRowModel(), - }, getRowId: (row) => row.id, tableComponents: { PaginationControls, @@ -97,7 +97,7 @@ export const { | Helper | Purpose | |---|---| -| `useAppTable` | Creates a table with shared features, row models, defaults, and registered components. | +| `useAppTable` | Creates a table with shared features (including row model factories), defaults, and registered components. | | `createAppColumnHelper` | Creates column helpers with `TFeatures` and registered component types already bound. | | `useTableContext` | Reads the current table inside registered table components. | | `useCellContext` | Reads the current cell inside registered cell components. | @@ -137,7 +137,7 @@ Registered cell components use `useCellContext()` internally, and registered hea ## Table Rendering -Create each table with `useAppTable`. You pass table-specific options like `key`, `columns`, and `data`; the shared `features`, `rowModels`, `getRowId`, and component registries come from the hook. +Create each table with `useAppTable`. You pass table-specific options like `key`, `columns`, and `data`; the shared `features`, `getRowId`, and component registries come from the hook. ```tsx const table = useAppTable( diff --git a/docs/framework/react/guide/custom-features.md b/docs/framework/react/guide/custom-features.md index b82c73403e..15d534d188 100644 --- a/docs/framework/react/guide/custom-features.md +++ b/docs/framework/react/guide/custom-features.md @@ -268,7 +268,6 @@ const features = tableFeatures({ densityPlugin }) const table = useTable({ features, - rowModels: {}, columns, data, //.. @@ -286,7 +285,6 @@ const [density, setDensity] = React.useState('md') const table = useTable({ features, - rowModels: {}, columns, data, //... diff --git a/docs/framework/react/guide/expanding.md b/docs/framework/react/guide/expanding.md index add8a46826..70a18da086 100644 --- a/docs/framework/react/guide/expanding.md +++ b/docs/framework/react/guide/expanding.md @@ -13,13 +13,13 @@ Want to skip to the implementation? Check out these React examples: ```tsx import { useTable, tableFeatures, rowExpandingFeature, createExpandedRowModel } from '@tanstack/react-table' -const features = tableFeatures({ rowExpandingFeature }) +const features = tableFeatures({ + rowExpandingFeature, + expandedRowModel: createExpandedRowModel(), +}) const table = useTable({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, columns, data, }) @@ -38,7 +38,7 @@ There are multiple use cases for expanding features in TanStack Table that will ### Enable Client-Side Expanding -To use the client-side expanding features, add the `rowExpandingFeature` to your features and the `expandedRowModel` to your row models: +To use the client-side expanding features, add the `rowExpandingFeature` and the `expandedRowModel` factory to your features: ```ts import { @@ -48,13 +48,13 @@ import { createExpandedRowModel, } from '@tanstack/react-table' -const features = tableFeatures({ rowExpandingFeature }) +const features = tableFeatures({ + rowExpandingFeature, + expandedRowModel: createExpandedRowModel(), +}) const table = useTable({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, // other options... }) ``` @@ -99,9 +99,6 @@ Then you can use the getSubRows function to return the children array in each ro ```ts const table = useTable({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, getSubRows: (row) => row.children, // return the children array as sub-rows // other options... }) @@ -121,9 +118,6 @@ import { Fragment } from 'react' //... const table = useTable({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, getRowCanExpand: (row) => true, // Add your logic to determine if a row can be expanded. True means all rows include expanded data // other options... }) @@ -168,7 +162,6 @@ const expanded = useSelector(expandedAtom) const table = useTable({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, // other options... atoms: { expanded: expandedAtom, // expanding APIs now update expandedAtom @@ -183,7 +176,6 @@ const [expanded, setExpanded] = useState({}) const table = useTable({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, // other options... state: { expanded, @@ -261,15 +253,17 @@ Use `table.setExpanded` to update the expanded state directly. `table.resetExpan By default, the filtering process starts from the parent rows and moves downwards. This means if a parent row is excluded by the filter, all its child rows will also be excluded. However, you can change this behavior by using the `filterFromLeafRows` option. When this option is enabled, the filtering process starts from the leaf (child) rows and moves upwards. This ensures that a parent row will be included in the filtered results as long as at least one of its child or grandchild rows meets the filter criteria. Additionally, you can control how deep into the child hierarchy the filter process goes by using the `maxLeafRowFilterDepth` option. This option allows you to specify the maximum depth of child rows that the filter should consider. ```ts -const features = tableFeatures({ columnFilteringFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnFilteringFeature, + rowExpandingFeature, + filteredRowModel: createFilteredRowModel(), + expandedRowModel: createExpandedRowModel(), + filterFns, +}) //... const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - expandedRowModel: createExpandedRowModel(), - }, getSubRows: (row) => row.subRows, filterFromLeafRows: true, // search through the expanded rows maxLeafRowFilterDepth: 1, // limit the depth of the expanded rows that are searched @@ -284,7 +278,6 @@ By default, expanded rows are paginated along with the rest of the table (which ```ts const table = useTable({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, // other options... paginateExpandedRows: false, }) @@ -307,7 +300,6 @@ A common reason to set `autoResetExpanded: false` is editing data while viewing ```ts const table = useTable({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, // other options... autoResetExpanded: false, // keep expanded state when data changes // autoResetAll: false, // or turn off all auto resets at once @@ -323,7 +315,6 @@ const features = tableFeatures({ rowExpandingFeature }) const table = useTable({ features, - rowModels: {}, // no expandedRowModel needed for manual expanding // other options... manualExpanding: true, }) diff --git a/docs/framework/react/guide/fuzzy-filtering.md b/docs/framework/react/guide/fuzzy-filtering.md index 6f01fa9de2..309cd5dd5d 100644 --- a/docs/framework/react/guide/fuzzy-filtering.md +++ b/docs/framework/react/guide/fuzzy-filtering.md @@ -11,20 +11,21 @@ Want to skip to the implementation? Check out these React examples: ### React Setup ```tsx -import { useTable, tableFeatures, columnFilteringFeature, globalFilteringFeature, rowSortingFeature, createFilteredRowModel, createSortedRowModel, filterFns, sortFns } from '@tanstack/react-table' +import { useTable, tableFeatures, columnFilteringFeature, globalFilteringFeature, rowSortingFeature, createFilteredRowModel, createSortedRowModel, filterFns, sortFns, metaHelper } from '@tanstack/react-table' const features = tableFeatures({ columnFilteringFeature, globalFilteringFeature, rowSortingFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + sortFns: { ...sortFns, fuzzy: fuzzySort }, + filterMeta: metaHelper(), }) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data, }) @@ -45,14 +46,21 @@ Using the match-sorter libraries is optional, but the TanStack Match Sorter Util ### Defining a Custom Fuzzy Filter Function -Here's an example of a custom fuzzy filter function: +Here's an example of a custom fuzzy filter function. First, define the filter meta shape and a features type that includes it: ```typescript import { rankItem } from '@tanstack/match-sorter-utils' import type { RankingInfo } from '@tanstack/match-sorter-utils' -import type { FilterFn, RowData } from '@tanstack/react-table' +import type { FilterFn, RowData, TableFeatures } from '@tanstack/react-table' + +interface FuzzyFilterMeta { + itemRank?: RankingInfo +} + +// A features type that carries the filterMeta shape +type FuzzyFeatures = TableFeatures & { filterMeta: FuzzyFilterMeta } -const fuzzyFilter: FilterFn = ( +const fuzzyFilter: FilterFn = ( row, columnId, value, @@ -71,23 +79,28 @@ const fuzzyFilter: FilterFn = ( In this function, we're using the `rankItem` function from the `@tanstack/match-sorter-utils` library to rank the item. We then store the ranking information in the filter meta of the row (the `addMeta` callback is optional, so call it with optional chaining), and return whether the item passed the ranking criteria. -To reference this filter function by the string name `'fuzzy'` (and to type the stored filter meta), augment the `FilterFns` and `FilterMeta` interfaces with a `declare module` block: +To reference this filter function by the string name `'fuzzy'` and type the stored filter meta, register both in the `tableFeatures` call using the `filterFns` and `filterMeta` slots: ```typescript -declare module '@tanstack/react-table' { - // add the fuzzy filter to the filterFns registry types - interface FilterFns { - fuzzy: FilterFn - } - interface FilterMeta { - itemRank?: RankingInfo - } -} +import { tableFeatures, metaHelper } from '@tanstack/react-table' + +const features = tableFeatures({ + columnFilteringFeature, + globalFilteringFeature, + rowSortingFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + sortFns: { ...sortFns, fuzzy: fuzzySort }, + filterMeta: metaHelper(), +}) ``` +No `declare module` augmentation is needed. The `filterFns` and `filterMeta` slots are scoped to this features object and only affect tables created with it. + ### Using Fuzzy Filtering with Global Filtering -To use fuzzy filtering with global filtering, register the fuzzy filter function in the registry passed to `createFilteredRowModel` and reference it in the `globalFilterFn` option of the table: +To use fuzzy filtering with global filtering, register the fuzzy filter function in the `filterFns` slot on `tableFeatures` and reference it in the `globalFilterFn` option of the table: ```typescript import { @@ -100,23 +113,22 @@ import { createSortedRowModel, filterFns, sortFns, + metaHelper, } from '@tanstack/react-table' const features = tableFeatures({ columnFilteringFeature, globalFilteringFeature, rowSortingFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), // needed if you want sorting with fuzzy rank + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + sortFns: { ...sortFns, fuzzy: fuzzySort }, + filterMeta: metaHelper(), }) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel({ - ...filterFns, - fuzzy: fuzzyFilter, - }), - sortedRowModel: createSortedRowModel(sortFns), // needed if you want sorting with fuzzy rank - }, columns, data, globalFilterFn: 'fuzzy', @@ -151,7 +163,7 @@ import { compareItems } from '@tanstack/match-sorter-utils' import { sortFns } from '@tanstack/react-table' import type { SortFn } from '@tanstack/react-table' -const fuzzySort: SortFn = (rowA, rowB, columnId) => { +const fuzzySort: SortFn = (rowA, rowB, columnId) => { let dir = 0 // Only sort by rank if the column has ranking information @@ -169,7 +181,7 @@ const fuzzySort: SortFn = (rowA, rowB, columnId) => { In this function, we're comparing the ranking information of the two rows. If the ranks are equal, we fall back to alphanumeric sorting. -You can then pass this sorting function directly to the `sortFn` option of the column definition: +Register `fuzzySort` in the `sortFns` slot on `tableFeatures` (as shown in the setup section above) and reference it by name in the column definition: ```typescript { @@ -177,9 +189,9 @@ You can then pass this sorting function directly to the `sortFn` option of the c id: 'fullName', header: 'Full Name', cell: info => info.getValue(), - filterFn: 'fuzzy', // using our custom fuzzy filter function (registered above) - sortFn: fuzzySort, // pass our custom fuzzy sort function directly + filterFn: 'fuzzy', // using our custom fuzzy filter function (registered in features) + sortFn: 'fuzzy', // using our custom fuzzy sort function (registered in features) } ``` -> **Note:** Unlike `filterFn: 'fuzzy'` above, `fuzzySort` is passed as a function rather than a string. A string reference like `sortFn: 'fuzzySort'` would only work if you also registered the function in the registry passed to `createSortedRowModel` (e.g. `createSortedRowModel({ ...sortFns, fuzzySort })`) and augmented the `SortFns` interface the same way as `FilterFns`. Passing the function directly skips both steps. +You can also pass `fuzzySort` directly as a function to the `sortFn` column option instead of a string reference, which skips the registration step. diff --git a/docs/framework/react/guide/global-filtering.md b/docs/framework/react/guide/global-filtering.md index 63cb907964..8687f17f98 100644 --- a/docs/framework/react/guide/global-filtering.md +++ b/docs/framework/react/guide/global-filtering.md @@ -12,15 +12,17 @@ Want to skip to the implementation? Check out these React examples: ### React Setup ```tsx -import { useTable, tableFeatures, globalFilteringFeature, createFilteredRowModel, filterFns } from '@tanstack/react-table' +import { useTable, tableFeatures, columnFilteringFeature, globalFilteringFeature, createFilteredRowModel, filterFns } from '@tanstack/react-table' -const features = tableFeatures({ globalFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + globalFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, columns, data, }) @@ -52,20 +54,20 @@ If you're not sure, you can always start with client-side filtering and paginati If you have decided that you need to implement server-side global filtering instead of using the built-in client-side global filtering, here's how you do that. -No `filteredRowModel` is needed for manual server-side global filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to `rowModels`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. +No `filteredRowModel` is needed for manual server-side global filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to the features object, you can tell the table to skip it by setting the `manualFiltering` option to `true`. ```tsx import { useTable, tableFeatures, + columnFilteringFeature, globalFilteringFeature, } from '@tanstack/react-table' -const features = tableFeatures({ globalFilteringFeature }) +const features = tableFeatures({ columnFilteringFeature, globalFilteringFeature }) const table = useTable({ features, - rowModels: {}, // no filteredRowModel needed for manual server-side global filtering data, columns, manualFiltering: true, @@ -76,38 +78,38 @@ Note: When using manual global filtering, many of the options that are discussed ### Client-Side Global Filtering -If you are using the built-in client-side global filtering, add the `globalFilteringFeature` to your features and the `filteredRowModel` to your row models: +If you are using the built-in client-side global filtering, add the `globalFilteringFeature` (along with its required `columnFilteringFeature` prerequisite) and the `filteredRowModel` factory to your features: ```tsx import { useTable, tableFeatures, + columnFilteringFeature, globalFilteringFeature, createFilteredRowModel, filterFns, } from '@tanstack/react-table' -const features = tableFeatures({ globalFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + globalFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, // other options... }) ``` ### Global Filter Function -The `globalFilterFn` option allows you to specify the filter function that will be used for global filtering. The filter function can be a string that references a built-in filter function, a string that references a custom filter function registered in the registry passed to `createFilteredRowModel`, or a custom filter function passed directly. +The `globalFilterFn` option allows you to specify the filter function that will be used for global filtering. The filter function can be a string that references a built-in filter function, a string that references a custom filter function registered in the `filterFns` slot on `tableFeatures`, or a custom filter function passed directly. ```tsx const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, data, columns, globalFilterFn: 'includesString', // built-in filter function @@ -147,7 +149,6 @@ const globalFilter = useSelector(globalFilterAtom) const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... atoms: { globalFilter: globalFilterAtom, // table.setGlobalFilter now updates globalFilterAtom @@ -162,7 +163,6 @@ const [globalFilter, setGlobalFilter] = useState('') const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... state: { globalFilter, @@ -200,7 +200,6 @@ const customFilterFn = (row, columnId, filterValue) => { const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... globalFilterFn: customFilterFn, }) @@ -213,7 +212,6 @@ If you want to set an initial global filter state when the table is initialized, ```tsx const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... initialState: { globalFilter: 'search term', // if not controlling globalFilter state, set initial state here @@ -241,7 +239,6 @@ const columns = [ //... const table = useTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... columns, enableGlobalFilter: false, // disable global filtering for all columns diff --git a/docs/framework/react/guide/grouping.md b/docs/framework/react/guide/grouping.md index cd0cb5babd..ad4d71563e 100644 --- a/docs/framework/react/guide/grouping.md +++ b/docs/framework/react/guide/grouping.md @@ -13,13 +13,14 @@ Want to skip to the implementation? Check out these React examples: ```tsx import { useTable, tableFeatures, columnGroupingFeature, createGroupedRowModel, aggregationFns } from '@tanstack/react-table' -const features = tableFeatures({ columnGroupingFeature }) +const features = tableFeatures({ + columnGroupingFeature, + groupedRowModel: createGroupedRowModel(), + aggregationFns, +}) const table = useTable({ features, - rowModels: { - groupedRowModel: createGroupedRowModel(aggregationFns), - }, columns, data, }) @@ -35,7 +36,7 @@ Grouping can also affect column order. There are 3 table features that can reord 2. Manual [Column Ordering](./column-ordering) - A manually specified column order is applied. 3. **Grouping** - If grouping is enabled, a grouping state is active, and `tableOptions.groupedColumnMode` is set to `'reorder' | 'remove'`, then the grouped columns are reordered to the start of the column flow. -To use the grouping feature, add the `columnGroupingFeature` to your features and the `groupedRowModel` to your row models. The grouped row model is responsible for grouping the rows based on the grouping state. +To use the grouping feature, add the `columnGroupingFeature` and the `groupedRowModel` factory to your features. The grouped row model is responsible for grouping the rows based on the grouping state. ```tsx import { @@ -46,13 +47,14 @@ import { aggregationFns, } from '@tanstack/react-table' -const features = tableFeatures({ columnGroupingFeature }) +const features = tableFeatures({ + columnGroupingFeature, + groupedRowModel: createGroupedRowModel(), + aggregationFns, +}) const table = useTable({ features, - rowModels: { - groupedRowModel: createGroupedRowModel(aggregationFns), - }, // other options... }) ``` @@ -61,14 +63,16 @@ When grouping state is active, the table will add matching rows as subRows to th To allow the user to expand and collapse the grouped rows, you can use the expanding feature. ```tsx -const features = tableFeatures({ columnGroupingFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnGroupingFeature, + rowExpandingFeature, + groupedRowModel: createGroupedRowModel(), + expandedRowModel: createExpandedRowModel(), + aggregationFns, +}) const table = useTable({ features, - rowModels: { - groupedRowModel: createGroupedRowModel(aggregationFns), - expandedRowModel: createExpandedRowModel(), - }, // other options... }) ``` @@ -92,7 +96,6 @@ By default, when a column is grouped, it is moved to the start of the table. You ```tsx const table = useTable({ features, - rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) }, // other options... groupedColumnMode: 'reorder', }) @@ -128,21 +131,23 @@ There are several built-in aggregation functions that you can use: You can define custom aggregation functions in the registry that you pass to `createGroupedRowModel`. The registry is a record where the keys are the names of the aggregation functions, and the values are the aggregation functions themselves. You can then reference these aggregation functions by name in a column's `aggregationFn` option. ```tsx +const myCustomAggregation = (columnId, leafRows, childRows) => { + // return the aggregated value +} + +const features = tableFeatures({ + columnGroupingFeature, + groupedRowModel: createGroupedRowModel(), + aggregationFns: { ...aggregationFns, myCustomAggregation }, +}) + const table = useTable({ features, - rowModels: { - groupedRowModel: createGroupedRowModel({ - ...aggregationFns, - myCustomAggregation: (columnId, leafRows, childRows) => { - // return the aggregated value - }, - }), - }, // other options... }) ``` -In the above example, myCustomAggregation is a custom aggregation function that takes the column ID, the leaf rows, and the child rows, and returns the aggregated value. You can then use this aggregation function in a column's aggregationFn option: +In the above example, `myCustomAggregation` is a custom aggregation function that takes the column ID, the leaf rows, and the child rows, and returns the aggregated value. You can then use this aggregation function in a column's `aggregationFn` option: ```tsx const column = columnHelper.accessor('key', { @@ -150,17 +155,7 @@ const column = columnHelper.accessor('key', { }) ``` -> **TypeScript Note:** For `aggregationFn: 'myCustomAggregation'` string references to typecheck, augment the `AggregationFns` interface with a `declare module` block: -> -> ```tsx -> declare module '@tanstack/react-table' { -> interface AggregationFns { -> myCustomAggregation: AggregationFn -> } -> } -> ``` -> -> Alternatively, skip the registry and the augmentation entirely by passing the function directly to the `aggregationFn` column option. +> **TypeScript Note:** For `aggregationFn: 'myCustomAggregation'` string references to typecheck, register the function in the `aggregationFns` slot on `tableFeatures` (as shown above). Alternatively, skip the registry entirely by passing the function directly to the `aggregationFn` column option. ### Manual Grouping @@ -171,7 +166,6 @@ const features = tableFeatures({ columnGroupingFeature }) const table = useTable({ features, - rowModels: {}, // no groupedRowModel needed for manual grouping // other options... manualGrouping: true, }) @@ -194,7 +188,6 @@ const grouping = useSelector(groupingAtom) const table = useTable({ features, - rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) }, // other options... atoms: { grouping: groupingAtom, // grouping APIs now update groupingAtom @@ -209,7 +202,6 @@ const [grouping, setGrouping] = useState([]) const table = useTable({ features, - rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) }, // other options... state: { grouping, diff --git a/docs/framework/react/guide/migrating.md b/docs/framework/react/guide/migrating.md index b01d1b1d7e..0328fd14e7 100644 --- a/docs/framework/react/guide/migrating.md +++ b/docs/framework/react/guide/migrating.md @@ -2,27 +2,37 @@ title: Migrating to TanStack Table v9 (React) --- +> [!NOTE] +> `v9.0.0-beta.10` introduces a breaking change in how row models are defined in order to bring increased type-safety features. Row model factories and function registries now live as slots on the `features` object instead of a separate `rowModels` option, and the factories no longer take arguments. If you migrated on an earlier beta, see the [Row Model Factories](#row-model-factories) section below for the new shape. + ## What's New in TanStack Table v9 TanStack Table v9 is a major release that introduces significant architectural improvements while maintaining the core table logic you're familiar with. Here are the key changes: -### 1. Tree-shaking +### 1. Tree Shaking and Extensibility - **Features are tree-shakeable**: Features are now treated as plugins, so you import only what you use. If your table only needs sorting, you won't ship filtering, pagination, or other feature code. Bundlers can eliminate unused code, so for smaller tables you can expect to bundle ~6–7kb compared to 15–20kb for the same table in v8. This also lets TanStack Table add more features over time without bloating everyone's bundles. -- **Row models and their functions are refactored**: Row model factories (`createFilteredRowModel`, `createSortedRowModel`, etc.) now accept their processing functions (`filterFns`, `sortFns`, `aggregationFns`) as parameters. This enables tree-shaking of the functions themselves: if you use a custom filter, you don't pay for built-in filters you never use. +- **Row models and their functions are refactored**: Row model factories (`createFilteredRowModel`, `createSortedRowModel`, etc.) are now slots on the `features` object, and their processing functions (`filterFns`, `sortFns`, `aggregationFns`) are registered as their own feature slots. This enables tree-shaking of the functions themselves: if you only register a custom filter, you don't pay for built-in filters you never use. +- **Custom feature plugins with full type safety**: The same plugin architecture that powers the built-in features is open to your own code. Write a custom feature with its own state, options, and APIs, register it in `tableFeatures()` alongside the built-ins, and the table's types pick it all up automatically. See the [Custom Features Guide](./custom-features.md). ### 2. State Management -- **Uses TanStack Store**: The internal state system has been rebuilt on [TanStack Store](https://tanstack.com/store), providing a reactive, framework-agnostic foundation. This works similarly to TanStack Form's state model. +- **Compatible with the React Compiler**: The internal state system has been rebuilt on [TanStack Store](https://tanstack.com/store), providing a reactive, framework-agnostic foundation that works correctly under the React Compiler. This works similarly to TanStack Form's state model. - **Three-layer atom architecture**: Each state slice (sorting, pagination, rowSelection, etc.) lives in its own [atom](https://tanstack.com/store/latest/docs/quick-start) rather than a single monolithic state object. Internally, the library writes to per-slice `baseAtoms`; reads go through derived `table.atoms` and the flat `table.store`. This enables fine-grained reactivity, where components can subscribe to just the slices they care about. - **Default full-state subscription, optional narrower selectors**: By default, `useTable` selects all registered table state, so `table.state` contains the full state and the component re-renders when any registered state changes. Pass a narrower selector or use `table.Subscribe` when only part of the UI should re-render. - **Bring your own atoms (optional)**: For advanced use cases, you can own individual state slices by passing your own writable atoms via the new `atoms` option. This is great for sharing a slice across components or integrating with other atom-based tools. Precedence: `options.atoms[key]` > `options.state[key]` > internal `baseAtoms[key]`. ### 3. Composability -- **`tableOptions`**: New utilities let you compose and share table configurations. Define `features`, `rowModels`, and default options once, then reuse them across tables or pass them through `createTableHook`. +- **`tableOptions`**: New utilities let you compose and share table configurations. Define `features` (including row model factories), and default options once, then reuse them across tables or pass them through `createTableHook`. - **`createTableHook`** (optional, advanced): Create custom table hooks with pre-bound features, row models, and components, similar to TanStack Form's `createFormHook`. Define your table setup once and reuse it across many tables. You don't need this for most use cases; `useTable` is sufficient. +### 4. Improved Type Safety (No More Declaration Merging) + +- **Function registries replace `declare module` augmentation**: Custom filter, sort, and aggregation functions are registered by name in the `filterFns` / `sortFns` / `aggregationFns` slots on `tableFeatures()`. The registered keys become the valid, type-safe string values for `filterFn`, `sortFn`, `globalFilterFn`, and `aggregationFn` in your column definitions, with full inference. No more augmenting the `FilterFns` / `SortFns` / `AggregationFns` interfaces globally. +- **Per-table meta slots**: The type-only `tableMeta`, `columnMeta`, and `filterMeta` slots declare meta types for a single table instead of merging into a global interface. The `filterMeta` slot types both the `addMeta` callback in filter functions and the values read back from `row.columnFiltersMeta`. +- **Feature-gated APIs and validated prerequisites**: APIs like `table.setSorting` only exist on the table type when their feature is registered, and `tableFeatures()` validates slot prerequisites at the type level. Registering `sortFns` without `rowSortingFeature`, or `globalFilteringFeature` without `columnFilteringFeature`, is a typed error instead of a silent runtime no-op. + ### The Good News: Most Upgrades Are Opt-in While v9 is a significant upgrade, **you don't have to adopt everything at once**: @@ -31,7 +41,7 @@ While v9 is a significant upgrade, **you don't have to adopt everything at once* - **Don't want to think about tree-shaking?** Import `stockFeatures` to include all features, just like v8. - **Table markup is largely unchanged.** How you render `
`, etc. remains the same. -The main change is **how you define a table** with the Angular adapter, specifically the new `features` and `rowModels` options. +The main change is **how you define a table** with the Angular adapter, specifically the new `features` option and how row model factories are registered inside it. ## Core Breaking Changes @@ -53,11 +63,11 @@ const v9Table = injectTable(() => ({ ``` > Note: `injectTable` evaluates your initializer whenever any Angular signal read inside of it changes. -> Keep expensive/static values (like `columns`, `features`, and `rowModels`) as stable references outside the initializer. +> Keep expensive/static values (like `columns` and `features`) as stable references outside the initializer. -### New Required Options: `features` and `rowModels` +### New Required Option: `features` -In v9, you must explicitly declare which features and row models your table uses: +In v9, you must explicitly declare which features and row model factories your table uses via `tableFeatures`: ```ts // v8 @@ -80,7 +90,6 @@ const features = tableFeatures({}) // Empty = core features only // Define stable references outside the initializer const v9Table = injectTable(() => ({ features, - rowModels: {}, // Core row model is automatic columns: this.columns, data: this.data(), })) @@ -125,7 +134,6 @@ import { injectTable, stockFeatures } from '@tanstack/angular-table' class TableCmp { readonly table = injectTable(() => ({ features: stockFeatures, // All features included - rowModels: { /* ... */ }, columns: this.columns, data: this.data(), })) @@ -153,31 +161,31 @@ class TableCmp { --- -## The `rowModels` Option +## Row Model Factories -Row models are the functions that process your data (filtering, sorting, pagination, etc.). In v9, they're configured via `rowModels` instead of `get*RowModel` options. +Row models are the functions that process your data (filtering, sorting, pagination, etc.). In v9, row model factories and their `*Fns` registries move from a separate `rowModels` option into `tableFeatures`. ### Migration Mapping -| v8 Option | v9 `rowModels` Key | v9 Factory Function | -|-----------|---------------------|---------------------| +| v8 Option | v9 `tableFeatures` slot | v9 Factory Function | +|-----------|--------------------------|---------------------| | `getCoreRowModel()` | (automatic) | Not needed, always included | -| `getFilteredRowModel()` | `filteredRowModel` | `createFilteredRowModel(filterFns)` | -| `getSortedRowModel()` | `sortedRowModel` | `createSortedRowModel(sortFns)` | +| `getFilteredRowModel()` | `filteredRowModel` | `createFilteredRowModel()` | +| `getSortedRowModel()` | `sortedRowModel` | `createSortedRowModel()` | | `getPaginationRowModel()` | `paginatedRowModel` | `createPaginatedRowModel()` | | `getExpandedRowModel()` | `expandedRowModel` | `createExpandedRowModel()` | -| `getGroupedRowModel()` | `groupedRowModel` | `createGroupedRowModel(aggregationFns)` | +| `getGroupedRowModel()` | `groupedRowModel` | `createGroupedRowModel()` | | `getFacetedRowModel()` | `facetedRowModel` | `createFacetedRowModel()` | | `getFacetedMinMaxValues()` | `facetedMinMaxValues` | `createFacetedMinMaxValues()` | | `getFacetedUniqueValues()` | `facetedUniqueValues` | `createFacetedUniqueValues()` | -### Key Change: Row Model Functions Now Accept Parameters +The `filterFns`, `sortFns`, and `aggregationFns` objects are now registered as named slots on `tableFeatures` rather than passed as arguments to the factory functions. -Several row model factories now accept their processing functions as parameters. This enables better tree-shaking and explicit configuration: +### Key Change: Row Model Factories and Fn Registries Move into `tableFeatures` ```ts import { - injectTable, + tableFeatures, createFilteredRowModel, createSortedRowModel, createGroupedRowModel, @@ -187,15 +195,23 @@ import { aggregationFns, // Built-in aggregation functions } from '@tanstack/angular-table' +const features = tableFeatures({ + columnFilteringFeature, + rowSortingFeature, + columnGroupingFeature, + rowPaginationFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), + groupedRowModel: createGroupedRowModel(), + paginatedRowModel: createPaginatedRowModel(), + filterFns, + sortFns, + aggregationFns, +}) + class TableCmp { readonly table = injectTable(() => ({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - sortedRowModel: createSortedRowModel(sortFns), - groupedRowModel: createGroupedRowModel(aggregationFns), - paginatedRowModel: createPaginatedRowModel(), - }, columns: this.columns, data: this.data(), })) @@ -207,7 +223,7 @@ class TableCmp { ```ts // v8 import { - injectTable, + createAngularTable, getCoreRowModel, getFilteredRowModel, getSortedRowModel, @@ -245,15 +261,15 @@ const features = tableFeatures({ columnFilteringFeature, rowSortingFeature, rowPaginationFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), + paginatedRowModel: createPaginatedRowModel(), + filterFns, + sortFns, }) const v9Table = injectTable(() => ({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), - }, columns, data: data(), })) @@ -300,7 +316,6 @@ import { shallow } from '@tanstack/angular-table' class TableCmp { readonly table = injectTable(() => ({ features, - rowModels: { /* ... */ }, columns: this.columns, data: this.data(), })) @@ -331,7 +346,6 @@ import { shallow } from '@tanstack/angular-table' class TableCmp { readonly table = injectTable(() => ({ features, - rowModels: { /* ... */ }, columns: this.columns, data: this.data(), })) @@ -366,7 +380,6 @@ class TableCmp { readonly table = injectTable(() => ({ features, - rowModels: { /* ... */ }, columns: this.columns, data: this.data(), state: { @@ -437,15 +450,16 @@ const columns = columnHelper.columns([ When using `createTableHook`, you get a pre-bound `createAppColumnHelper` that only requires `TData`: ```ts -import { createTableHook, tableFeatures, rowSortingFeature } from '@tanstack/angular-table' +import { createTableHook, tableFeatures, rowSortingFeature, createSortedRowModel, sortFns } from '@tanstack/angular-table' -const features = tableFeatures({ rowSortingFeature }) - -const { injectAppTable, createAppColumnHelper } = createTableHook({ - features, - rowModels: { /* ... */ }, +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, }) +const { injectAppTable, createAppColumnHelper } = createTableHook({ features }) + // TFeatures is already bound, only need TData! const columnHelper = createAppColumnHelper() ``` @@ -514,7 +528,6 @@ class TableCmp { ...baseOptions, columns: this.columns, data: this.data(), - rowModels: {}, })) } ``` @@ -538,26 +551,21 @@ import { const features = tableFeatures({ rowSortingFeature, columnFilteringFeature, + sortedRowModel: createSortedRowModel(), + filteredRowModel: createFilteredRowModel(), + sortFns, + filterFns, }) // Partial options without data or columns -const featureOptions = tableOptions({ - features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - filteredRowModel: createFilteredRowModel(filterFns), - }, -}) +const featureOptions = tableOptions({ features }) ``` ```ts -import { injectTable, tableOptions, createPaginatedRowModel } from '@tanstack/angular-table' +import { injectTable, tableOptions } from '@tanstack/angular-table' -// Another partial without features (inherits from spread) +// Another partial (inherits features from spread) const paginationDefaults = tableOptions({ - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, initialState: { pagination: { pageIndex: 0, pageSize: 25 }, }, @@ -589,16 +597,16 @@ import { sortFns, } from '@tanstack/angular-table' -const features = tableFeatures({ rowSortingFeature, rowPaginationFeature }) - -const sharedOptions = tableOptions({ - features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), - }, +const features = tableFeatures({ + rowSortingFeature, + rowPaginationFeature, + sortedRowModel: createSortedRowModel(), + paginatedRowModel: createPaginatedRowModel(), + sortFns, }) +const sharedOptions = tableOptions({ features }) + const { injectAppTable } = createTableHook(sharedOptions) ``` @@ -735,6 +743,39 @@ const features = tableFeatures({ See the new [Table and Column Meta Guide](../../../guide/table-and-column-meta) for full details on both approaches. +### `FilterFns`/`SortFns`/`AggregationFns`/`FilterMeta` Augmentation Replaced by Registry Slots + +In v8, making a custom function usable as a string reference (like `filterFn: 'fuzzy'`) required `declare module` augmentation of the `FilterFns` interface, and typing filter meta required augmenting `FilterMeta`. In v9, registering the function in the matching registry slot does both jobs with no global augmentation: + +```ts +// v8 +declare module '@tanstack/angular-table' { + interface FilterFns { + fuzzy: FilterFn + } + interface FilterMeta { + itemRank: RankingInfo + } +} + +// v9 - register in the slot; the key becomes a valid string value +interface FuzzyFilterMeta { + itemRank?: RankingInfo +} + +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + filterMeta: metaHelper(), +}) + +// 'fuzzy' now typechecks in column defs for tables using these features +columnHelper.accessor('name', { filterFn: 'fuzzy' }) +``` + +The same pattern applies to `sortFns` (for `sortFn` string values) and `aggregationFns` (for `aggregationFn` string values). See the [Fuzzy Filtering Guide](./fuzzy-filtering.md) for a complete example. + ### `RowData` Type Restriction The `RowData` type is now more restrictive: @@ -754,8 +795,9 @@ This change improves type safety. If you were passing unusual data types, ensure ## Migration Checklist - [ ] Update your table setup to v9 and define `features` using `tableFeatures()` (or use `stockFeatures`) -- [ ] Migrate `get*RowModel()` options to `rowModels` -- [ ] Update row model factories to include `Fns` parameters where needed +- [ ] Migrate `get*RowModel()` options: move row model factories into `tableFeatures` as named slots +- [ ] Move `filterFns`, `sortFns`, and `aggregationFns` into `tableFeatures` as named slots (no longer passed as factory arguments) +- [ ] Replace `declare module` augmentation of `FilterFns`/`SortFns`/`AggregationFns` with registry-slot registration, and `FilterMeta` augmentation with the `filterMeta` slot - [ ] Update TypeScript types to include `TFeatures` generic - [ ] Update state access: `table.getState().slice` → `table.atoms..get()` where possible; use `table.store.get()` for full-state/debug reads - [ ] Update `createColumnHelper()` → `createColumnHelper()` diff --git a/docs/framework/angular/guide/pagination.md b/docs/framework/angular/guide/pagination.md index b5c5d76c01..aa52701cf5 100644 --- a/docs/framework/angular/guide/pagination.md +++ b/docs/framework/angular/guide/pagination.md @@ -14,16 +14,16 @@ Want to skip to the implementation? Check out these Angular examples: import { signal } from '@angular/core' import { injectTable, tableFeatures, rowPaginationFeature, createPaginatedRowModel } from '@tanstack/angular-table' -const features = tableFeatures({ rowPaginationFeature }) +const features = tableFeatures({ + rowPaginationFeature, + paginatedRowModel: createPaginatedRowModel(), +}) export class App { readonly data = signal(defaultData) readonly table = injectTable(() => ({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data: this.data(), })) @@ -58,7 +58,7 @@ Alternatively, instead of paginating the data, you can render all rows of a larg #### Pagination Row Model -If you want to take advantage of the built-in client-side pagination in TanStack Table, add the `rowPaginationFeature` to your features and the `paginatedRowModel` to your row models: +If you want to take advantage of the built-in client-side pagination in TanStack Table, add the `rowPaginationFeature` and the `paginatedRowModel` factory to your features: ```ts import { @@ -68,13 +68,13 @@ import { createPaginatedRowModel, } from '@tanstack/angular-table' -const features = tableFeatures({ rowPaginationFeature }) +const features = tableFeatures({ + rowPaginationFeature, + paginatedRowModel: createPaginatedRowModel(), +}) readonly table = injectTable(() => ({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, })) @@ -101,7 +101,6 @@ const features = tableFeatures({ rowPaginationFeature }) readonly table = injectTable(() => ({ features, - rowModels: {}, // no paginatedRowModel needed for server-side pagination columns, data, manualPagination: true, // turn off client-side pagination @@ -145,9 +144,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data: this.data(), atoms: { @@ -169,9 +165,6 @@ readonly pagination = signal({ readonly table = injectTable(() => ({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data: this.data(), onPaginationChange: (updater) => @@ -189,9 +182,6 @@ Alternatively, if you have no need for managing the `pagination` state in your o ```ts readonly table = injectTable(() => ({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, initialState: { @@ -216,9 +206,6 @@ By default, `pageIndex` is reset to `0` whenever the client-side row models reco ```ts readonly table = injectTable(() => ({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, autoResetPageIndex: false, // turn off auto reset of pageIndex diff --git a/docs/framework/angular/guide/row-pinning.md b/docs/framework/angular/guide/row-pinning.md index b8893a4d8d..f9ebb2fa41 100644 --- a/docs/framework/angular/guide/row-pinning.md +++ b/docs/framework/angular/guide/row-pinning.md @@ -21,7 +21,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data: this.data(), })) @@ -39,7 +38,7 @@ There are 2 table features that can reorder rows, which happen in the following ### Enable Row Pinning -To use row pinning, add `rowPinningFeature` to your features. Row pinning does not require a row model factory, so `rowModels` can stay empty unless your table uses other row-model features. +To use row pinning, add `rowPinningFeature` to your features. Row pinning does not require a row model factory. ```ts import { @@ -52,7 +51,6 @@ const features = tableFeatures({ rowPinningFeature }) readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data, })) @@ -74,7 +72,6 @@ You can pin rows by default with `initialState.rowPinning`: ```ts readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data, initialState: { @@ -100,7 +97,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data: this.data(), atoms: { @@ -122,7 +118,6 @@ readonly rowPinning = signal({ readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data, state: { @@ -222,7 +217,6 @@ By default, all rows can be pinned. You can disable row pinning for the whole ta ```ts readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data, enableRowPinning: row => row.original.status !== 'archived', @@ -238,7 +232,6 @@ Set `keepPinnedRows` to `false` if pinned rows should only render when they are ```ts readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data, keepPinnedRows: false, diff --git a/docs/framework/angular/guide/row-selection.md b/docs/framework/angular/guide/row-selection.md index 3a86f22d70..349fb8dc23 100644 --- a/docs/framework/angular/guide/row-selection.md +++ b/docs/framework/angular/guide/row-selection.md @@ -22,7 +22,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data: this.data(), })) @@ -69,7 +68,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: {}, //... atoms: { rowSelection: this.rowSelectionAtom, // selection APIs now update rowSelectionAtom @@ -87,7 +85,6 @@ readonly rowSelection = signal({}) readonly table = injectTable(() => ({ features, - rowModels: {}, //... onRowSelectionChange: (updater) => typeof updater === 'function' @@ -106,7 +103,6 @@ By default, the row id for each row is simply the `row.index`. If you are using ```ts readonly table = injectTable(() => ({ features, - rowModels: {}, //... getRowId: (row) => row.uuid, // use the row's uuid from your database as the row id })) diff --git a/docs/framework/angular/guide/sorting.md b/docs/framework/angular/guide/sorting.md index 205250d70f..515e0b906f 100644 --- a/docs/framework/angular/guide/sorting.md +++ b/docs/framework/angular/guide/sorting.md @@ -14,16 +14,17 @@ Want to skip to the implementation? Check out these Angular examples: import { signal } from '@angular/core' import { injectTable, tableFeatures, rowSortingFeature, createSortedRowModel, sortFns } from '@tanstack/angular-table' -const features = tableFeatures({ rowSortingFeature }) +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, +}) export class App { readonly data = signal(defaultData) readonly table = injectTable(() => ({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data: this.data(), })) @@ -55,7 +56,6 @@ You can access the sorting state directly from the table instance with `table.at ```ts readonly table = injectTable(() => ({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, //... @@ -79,7 +79,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data: this.data(), //... @@ -100,7 +99,6 @@ readonly sorting = signal([]) //... readonly table = injectTable(() => ({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data: this.data(), //... @@ -121,7 +119,6 @@ If you do not need to control the sorting state in your own state management or ```ts readonly table = injectTable(() => ({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, //... @@ -156,7 +153,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: {}, // no sortedRowModel needed for manual sorting columns, data: this.data(), manualSorting: true, // use pre-sorted row model instead of sorted row model @@ -175,7 +171,7 @@ Hoisting the sorting state into your own scope (with an external atom or the `st ### Client-Side Sorting -To implement client-side sorting, add the `rowSortingFeature` to your features and the `sortedRowModel` to your row models. Import `createSortedRowModel` and `sortFns` from TanStack Table: +To implement client-side sorting, add the `rowSortingFeature` and the `sortedRowModel` factory to your features. Import `createSortedRowModel` and `sortFns` from TanStack Table: ```ts import { @@ -186,13 +182,14 @@ import { sortFns, } from '@tanstack/angular-table' -const features = tableFeatures({ rowSortingFeature }) +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, +}) readonly table = injectTable(() => ({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data, })) @@ -237,6 +234,22 @@ Every sorting function receives 2 rows and a column ID and are expected to compa | `1` | `a > b` | ```ts +const myCustomSortFn: SortFn = (rowA, rowB, columnId) => + rowA.original[columnId] > rowB.original[columnId] + ? 1 + : rowA.original[columnId] < rowB.original[columnId] + ? -1 + : 0 + +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns: { + ...sortFns, + myCustomSortFn, + }, +}) + const columns = [ { header: () => 'Name', @@ -246,7 +259,7 @@ const columns = [ { header: () => 'Age', accessorKey: 'age', - sortFn: 'myCustomSortFn', // reference a custom sorting function registered with createSortedRowModel + sortFn: 'myCustomSortFn', // reference a custom sorting function registered in the features sortFns slot }, { header: () => 'Birthday', @@ -265,33 +278,12 @@ const columns = [ //... readonly table = injectTable(() => ({ features, - rowModels: { - sortedRowModel: createSortedRowModel({ - ...sortFns, - myCustomSortFn: (rowA, rowB, columnId) => - rowA.original[columnId] > rowB.original[columnId] - ? 1 - : rowA.original[columnId] < rowB.original[columnId] - ? -1 - : 0, - }), - }, columns, data, })) ``` -> **TypeScript Note:** For `sortFn: 'myCustomSortFn'` string references to typecheck, augment the `SortFns` interface with a `declare module` block: -> -> ```ts -> declare module '@tanstack/angular-table' { -> interface SortFns { -> myCustomSortFn: SortFn -> } -> } -> ``` -> -> Alternatively, skip the registry and the augmentation entirely by passing the function directly to the `sortFn` column option. +> **TypeScript Note:** For `sortFn: 'myCustomSortFn'` string references to typecheck, register the function in the `sortFns` slot on `tableFeatures` (as shown above). Alternatively, skip the registry entirely by passing the function directly to the `sortFn` column option. ### Customize Sorting @@ -317,7 +309,6 @@ const columns = [ //... readonly table = injectTable(() => ({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, enableSorting: false, // disable sorting for the entire table @@ -345,7 +336,6 @@ const columns = [ //... readonly table = injectTable(() => ({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, sortDescFirst: true, //sort by all columns in descending order first (default is ascending for string columns and descending for number columns) @@ -412,7 +402,6 @@ Once a column is sorted and `enableSortingRemoval` is `false`, toggling the sort ```ts readonly table = injectTable(() => ({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, enableSortingRemoval: false, // disable the ability to remove sorting on columns (sorting can never return to 'none' once applied) @@ -439,7 +428,6 @@ const columns = [ //... readonly table = injectTable(() => ({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, enableMultiSort: false, // disable multi-sorting for the entire table @@ -453,7 +441,6 @@ By default, the `Shift` key is used to trigger multi-sorting. You can change thi ```ts readonly table = injectTable(() => ({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, isMultiSortEvent: (e) => true, // normal click triggers multi-sorting @@ -469,7 +456,6 @@ By default, there is no limit to the number of columns that can be sorted at onc ```ts readonly table = injectTable(() => ({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, maxMultiSortColCount: 3, // only allow 3 columns to be sorted at once @@ -483,7 +469,6 @@ By default, the ability to remove multi-sorts is enabled. You can disable this b ```ts readonly table = injectTable(() => ({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, enableMultiRemove: false, // disable the ability to remove multi-sorts diff --git a/docs/framework/angular/guide/table-state.md b/docs/framework/angular/guide/table-state.md index f11866a241..59082a71ba 100644 --- a/docs/framework/angular/guide/table-state.md +++ b/docs/framework/angular/guide/table-state.md @@ -46,14 +46,13 @@ State slices are only created for the features that are registered in `features` const features = tableFeatures({ rowPaginationFeature, rowSortingFeature, + paginatedRowModel: createPaginatedRowModel(), + sortedRowModel: createSortedRowModel(), + sortFns, }) readonly table = injectTable(() => ({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data: this.data(), })) @@ -103,9 +102,6 @@ import { shallow } from '@tanstack/angular-table' readonly table = injectTable(() => ({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data: this.data(), })) @@ -165,10 +161,6 @@ If you only need to customize the starting value for some table state, use `init ```ts readonly table = injectTable(() => ({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), - }, columns, data: this.data(), initialState: { @@ -246,7 +238,6 @@ export class App { readonly table = injectTable(() => ({ features, - rowModels: {}, columns, data: this.dataQuery.data()?.rows ?? [], rowCount: this.dataQuery.data()?.rowCount, @@ -277,10 +268,6 @@ readonly pagination = signal({ readonly table = injectTable(() => ({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), - }, columns, data: this.data(), state: { diff --git a/docs/framework/angular/guide/virtualization.md b/docs/framework/angular/guide/virtualization.md index 730a5b213a..dec554bfde 100644 --- a/docs/framework/angular/guide/virtualization.md +++ b/docs/framework/angular/guide/virtualization.md @@ -59,14 +59,13 @@ import { injectVirtualizer } from '@tanstack/angular-virtual' const features = tableFeatures({ columnSizingFeature, rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, }) export class App { readonly table = injectTable(() => ({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data: this.data(), })) diff --git a/docs/framework/angular/quick-start.md b/docs/framework/angular/quick-start.md index 287c8e2a84..5c8dde81c0 100644 --- a/docs/framework/angular/quick-start.md +++ b/docs/framework/angular/quick-start.md @@ -72,7 +72,6 @@ export class App { readonly table = injectTable(() => ({ key: 'person-table', // registers this table with the devtools features, - rowModels: {}, // the core row model is included by default columns, data: this.data(), })) @@ -116,7 +115,6 @@ export class App { A few things to note: - `tableFeatures({})` declares which optional features the table uses. Registering only what you need keeps bundles small and gives TypeScript accurate types for the table instance. -- `rowModels: {}` is fine for a basic table because the core row model is always included. Feature row models (sorting, filtering, pagination) are registered here when you need them. - `injectTable` must be called in an injection context. Its initializer re-runs when Angular signals read inside it change (like `this.data()` here), and the adapter syncs the table options. - The `FlexRender` directives (`*flexRenderHeader`, `*flexRenderCell`, `*flexRenderFooter`) render the `header`, `cell`, and `footer` definitions from your columns, whether they are plain values, templates, or components. See the [Rendering Guide](./guide/rendering.md) for `flexRenderComponent` and render context helpers. - The `key` option is optional unless you use the [TanStack Table Devtools](../../devtools). The devtools identify tables by `key`, and you register a table with `injectTanStackTableDevtools` from `@tanstack/angular-table-devtools`. @@ -125,7 +123,7 @@ See the full [Basic injectTable example](./examples/basic-inject-table) for a ru ## Add a Feature: Sorting -Features are opt-in in v9. To make columns sortable, register `rowSortingFeature` in `tableFeatures`, register a sorted row model under `rowModels`, and wire the header click handler in the template. +Features are opt-in in v9. To make columns sortable, register `rowSortingFeature` and the sorted row model in `tableFeatures`, then wire the header click handler in the template. ```ts // app.ts @@ -138,6 +136,8 @@ import { const features = tableFeatures({ rowSortingFeature, // enables sorting APIs and state + sortedRowModel: createSortedRowModel(), // client-side sorting + sortFns, }) export class App { @@ -146,9 +146,6 @@ export class App { readonly table = injectTable(() => ({ key: 'person-table', features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), // client-side sorting - }, columns, data: this.data(), })) @@ -186,7 +183,7 @@ export class App {
`, ``, ``, `
`, etc. remains the same. -The main change is **how you define a table** with the `useTable` hook, specifically the new `features` and `rowModels` options. +The main change is **how you define a table** with the `useTable` hook, specifically the new `features` option and where row model factories are registered. --- @@ -75,9 +85,9 @@ import { useTable } from '@tanstack/react-table' const table = useTable(options) ``` -### New Required Options: `features` and `rowModels` +### New Required Options: `features` and Row Model Factories -In v9, you must explicitly declare which features and row models your table uses: +In v9, you must explicitly declare which features your table uses. Row model factories now live on the features object instead of a separate `rowModels` option: ```tsx // v8 @@ -96,7 +106,6 @@ const features = tableFeatures({}) // Empty = core features only const table = useTable({ features, - rowModels: {}, // Core row model is automatic columns, data, }) @@ -140,7 +149,6 @@ import { useTable, stockFeatures } from '@tanstack/react-table' const table = useTable({ features: stockFeatures, // All features included - rowModels: { /* ... */ }, columns, data, }) @@ -167,30 +175,31 @@ const table = useTable({ --- -## The `rowModels` Option +## Row Model Factories -Row models are the functions that process your data (filtering, sorting, pagination, etc.). In v9, they're configured via `rowModels` instead of `get*RowModel` options. +Row models are the functions that process your data (filtering, sorting, pagination, etc.). In v9, row model factories live on the `tableFeatures({})` call rather than a separate `rowModels` option. The processing function registries (`filterFns`, `sortFns`, `aggregationFns`) are also registered on features. ### Migration Mapping -| v8 Option | v9 `rowModels` Key | v9 Factory Function | -|-----------|---------------------|---------------------| +| v8 Option | v9 `tableFeatures` Slot | v9 Factory Function | +|-----------|------------------------|---------------------| | `getCoreRowModel()` | (automatic) | Not needed, always included | -| `getFilteredRowModel()` | `filteredRowModel` | `createFilteredRowModel(filterFns)` | -| `getSortedRowModel()` | `sortedRowModel` | `createSortedRowModel(sortFns)` | +| `getFilteredRowModel()` | `filteredRowModel` | `createFilteredRowModel()` | +| `getSortedRowModel()` | `sortedRowModel` | `createSortedRowModel()` | | `getPaginationRowModel()` | `paginatedRowModel` | `createPaginatedRowModel()` | | `getExpandedRowModel()` | `expandedRowModel` | `createExpandedRowModel()` | -| `getGroupedRowModel()` | `groupedRowModel` | `createGroupedRowModel(aggregationFns)` | +| `getGroupedRowModel()` | `groupedRowModel` | `createGroupedRowModel()` | | `getFacetedRowModel()` | `facetedRowModel` | `createFacetedRowModel()` | | `getFacetedMinMaxValues()` | `facetedMinMaxValues` | `createFacetedMinMaxValues()` | | `getFacetedUniqueValues()` | `facetedUniqueValues` | `createFacetedUniqueValues()` | -### Key Change: Row Model Functions Now Accept Parameters +### Key Change: Row Model Factories and Fns Registries Move to `tableFeatures` -Several row model factories now accept their processing functions as parameters. This enables better tree-shaking and explicit configuration: +Row model factories and their processing function registries are now slots on `tableFeatures`. This enables better tree-shaking: you only bundle the row model code and filter/sort/aggregation functions you actually register. ```tsx import { + tableFeatures, createFilteredRowModel, createSortedRowModel, createGroupedRowModel, @@ -199,14 +208,22 @@ import { aggregationFns, // Built-in aggregation functions } from '@tanstack/react-table' +const features = tableFeatures({ + columnFilteringFeature, + rowSortingFeature, + columnGroupingFeature, + rowPaginationFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), + groupedRowModel: createGroupedRowModel(), + paginatedRowModel: createPaginatedRowModel(), + filterFns, + sortFns, + aggregationFns, +}) + const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - sortedRowModel: createSortedRowModel(sortFns), - groupedRowModel: createGroupedRowModel(aggregationFns), - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, }) @@ -255,15 +272,15 @@ const features = tableFeatures({ columnFilteringFeature, rowSortingFeature, rowPaginationFeature, + filteredRowModel: createFilteredRowModel(), // now called "create*RowModel()" on the features object + sortedRowModel: createSortedRowModel(), + paginatedRowModel: createPaginatedRowModel(), + filterFns, // fns registries move to features too + sortFns, }) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), // now called "create*RowModel()" with a Fns parameter - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, }) @@ -304,7 +321,6 @@ const { sorting, pagination } = table.store.state // v9 - via table.state (full selected state by default) const table = useTable({ features, - rowModels: { /* ... */ }, columns, data, }) @@ -330,8 +346,7 @@ The biggest state management improvement is `table.Subscribe`, which enables fin function MyTable() { const table = useTable({ features, - rowModels: { /* ... */ }, - columns, + columns, data, }) @@ -361,7 +376,6 @@ The default selector already gives v8-style behavior where the component re-rend ```tsx const table = useTable({ features, - rowModels: { /* ... */ }, columns, data, }) @@ -385,7 +399,6 @@ const [pagination, setPagination] = useState({ const table = useTable({ features, - rowModels: { /* ... */ }, columns, data, state: { @@ -449,10 +462,6 @@ function MyTable({ data, columns }) { const table = useTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, // Per-slice external atoms. The library writes directly to these, @@ -540,7 +549,6 @@ const features = tableFeatures({ rowSortingFeature }) const { useAppTable, createAppColumnHelper } = createTableHook({ features, - rowModels: { /* ... */ }, }) // TFeatures is already bound, only need TData! @@ -617,7 +625,6 @@ const table = useTable({ ...baseOptions, columns, data, - rowModels: {}, }) ``` @@ -626,25 +633,21 @@ const table = useTable({ `tableOptions()` allows you to omit certain required fields (like `data`, `columns`, or `features`) when creating partial configurations: ```tsx +// Row model factories and fns registries are registered on the features object const features = tableFeatures({ rowSortingFeature, columnFilteringFeature, + sortedRowModel: createSortedRowModel(), + filteredRowModel: createFilteredRowModel(), + sortFns, + filterFns, }) // Partial options without data or columns -const featureOptions = tableOptions({ - features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - filteredRowModel: createFilteredRowModel(filterFns), - }, -}) +const featureOptions = tableOptions({ features }) // Another partial without features (inherits from spread) const paginationDefaults = tableOptions({ - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, initialState: { pagination: { pageIndex: 0, pageSize: 25 }, }, @@ -664,16 +667,16 @@ const table = useTable({ `tableOptions()` pairs well with `createTableHook` for building composable table factories: ```tsx -const features = tableFeatures({ rowSortingFeature, rowPaginationFeature }) - -const sharedOptions = tableOptions({ - features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), - }, +const features = tableFeatures({ + rowSortingFeature, + rowPaginationFeature, + sortedRowModel: createSortedRowModel(), + paginatedRowModel: createPaginatedRowModel(), + sortFns, }) +const sharedOptions = tableOptions({ features }) + const { useAppTable } = createTableHook(sharedOptions) ``` @@ -683,7 +686,7 @@ const { useAppTable } = createTableHook(sharedOptions) **This is an advanced, optional feature.** You don't need to use `createTableHook`; `useTable` is sufficient for most use cases. If you're familiar with [TanStack Form](https://tanstack.com/form)'s `createFormHook`, `createTableHook` works almost the same way: it creates a custom hook with pre-bound configuration that you can reuse across many tables. -For applications with multiple tables sharing the same configuration, `createTableHook` lets you define features, row models, and reusable components once: +For applications with multiple tables sharing the same configuration, `createTableHook` lets you define features (including row model factories), and reusable components once: ```tsx // hooks/table.ts @@ -703,10 +706,16 @@ import { // Import your reusable components import { PaginationControls, SortIndicator, TextCell } from './components' +// Features and row model factories defined once const features = tableFeatures({ columnFilteringFeature, rowSortingFeature, rowPaginationFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), + paginatedRowModel: createPaginatedRowModel(), + filterFns, + sortFns, }) export const { @@ -716,16 +725,8 @@ export const { useCellContext, useHeaderContext, } = createTableHook({ - // Features defined once features, - // Row models defined once - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), - }, - // Default table options debugTable: process.env.NODE_ENV === 'development', @@ -755,7 +756,7 @@ function UsersTable({ data }: { data: Person[] }) { const table = useAppTable({ columns, data, - // features and rowModels already configured! + // features (including row model factories) already configured! }) return ( @@ -1023,6 +1024,39 @@ const features = tableFeatures({ See the new [Table and Column Meta Guide](../../../guide/table-and-column-meta) for full details on both approaches. +### `FilterFns`/`SortFns`/`AggregationFns`/`FilterMeta` Augmentation Replaced by Registry Slots + +In v8, making a custom function usable as a string reference (like `filterFn: 'fuzzy'`) required `declare module` augmentation of the `FilterFns` interface, and typing filter meta required augmenting `FilterMeta`. In v9, registering the function in the matching registry slot does both jobs with no global augmentation: + +```tsx +// v8 +declare module '@tanstack/react-table' { + interface FilterFns { + fuzzy: FilterFn + } + interface FilterMeta { + itemRank: RankingInfo + } +} + +// v9 - register in the slot; the key becomes a valid string value +interface FuzzyFilterMeta { + itemRank?: RankingInfo +} + +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + filterMeta: metaHelper(), +}) + +// 'fuzzy' now typechecks in column defs for tables using these features +columnHelper.accessor('name', { filterFn: 'fuzzy' }) +``` + +The same pattern applies to `sortFns` (for `sortFn` string values) and `aggregationFns` (for `aggregationFn` string values). See the [Fuzzy Filtering Guide](./fuzzy-filtering.md) for a complete example. + ### `RowData` Type Restriction The `RowData` type is now more restrictive: @@ -1043,8 +1077,9 @@ This change improves type safety. If you were passing unusual data types, ensure - [ ] Update import: `useReactTable` → `useTable` - [ ] Define `features` using `tableFeatures()` (or use `stockFeatures`) -- [ ] Migrate `get*RowModel()` options to `rowModels` -- [ ] Update row model factories to include `Fns` parameters where needed +- [ ] Migrate `get*RowModel()` options to `tableFeatures` slots (e.g. `filteredRowModel: createFilteredRowModel()`) +- [ ] Register `filterFns` / `sortFns` / `aggregationFns` registries as slots on `tableFeatures` (row model factories no longer take arguments) +- [ ] Replace `declare module` augmentation of `FilterFns`/`SortFns`/`AggregationFns` with registry-slot registration, and `FilterMeta` augmentation with the `filterMeta` slot - [ ] Update TypeScript types to include `TFeatures` generic - [ ] Update state access: `table.getState()` → `table.store.state` or `table.state` - [ ] Update `createColumnHelper()` → `createColumnHelper()` diff --git a/docs/framework/react/guide/pagination.md b/docs/framework/react/guide/pagination.md index 82eed32574..25f059f7a6 100644 --- a/docs/framework/react/guide/pagination.md +++ b/docs/framework/react/guide/pagination.md @@ -13,13 +13,13 @@ Want to skip to the implementation? Check out these React examples: ```tsx import { useTable, tableFeatures, rowPaginationFeature, createPaginatedRowModel } from '@tanstack/react-table' -const features = tableFeatures({ rowPaginationFeature }) +const features = tableFeatures({ + rowPaginationFeature, + paginatedRowModel: createPaginatedRowModel(), +}) const table = useTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, }) @@ -53,7 +53,7 @@ Alternatively, instead of paginating the data, you can render all rows of a larg #### Pagination Row Model -If you want to take advantage of the built-in client-side pagination in TanStack Table, add the `rowPaginationFeature` to your features and the `paginatedRowModel` to your row models: +If you want to take advantage of the built-in client-side pagination in TanStack Table, add the `rowPaginationFeature` and the `paginatedRowModel` factory to your features: ```tsx import { @@ -63,13 +63,13 @@ import { createPaginatedRowModel, } from '@tanstack/react-table' -const features = tableFeatures({ rowPaginationFeature }) +const features = tableFeatures({ + rowPaginationFeature, + paginatedRowModel: createPaginatedRowModel(), +}) const table = useTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, }) @@ -96,7 +96,6 @@ const features = tableFeatures({ rowPaginationFeature }) const table = useTable({ features, - rowModels: {}, // no paginatedRowModel needed for server-side pagination columns, data, manualPagination: true, // turn off client-side pagination @@ -142,9 +141,6 @@ const pagination = useSelector(paginationAtom) const table = useTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, atoms: { @@ -163,9 +159,6 @@ const [pagination, setPagination] = useState({ const table = useTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, onPaginationChange: setPagination, @@ -180,9 +173,6 @@ Alternatively, if you have no need for managing the `pagination` state in your o ```tsx const table = useTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, initialState: { @@ -207,9 +197,6 @@ By default, `pageIndex` is reset to `0` whenever the client-side row models reco ```tsx const table = useTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, autoResetPageIndex: false, // turn off auto reset of pageIndex diff --git a/docs/framework/react/guide/row-pinning.md b/docs/framework/react/guide/row-pinning.md index 53f786aa72..c072f5157f 100644 --- a/docs/framework/react/guide/row-pinning.md +++ b/docs/framework/react/guide/row-pinning.md @@ -16,7 +16,6 @@ const features = tableFeatures({ rowPinningFeature }) const table = useTable({ features, - rowModels: {}, columns, data, }) @@ -33,7 +32,7 @@ There are 2 table features that can reorder rows, which happen in the following ### Enable Row Pinning -To use row pinning, add `rowPinningFeature` to your features. Row pinning does not require a row model factory, so `rowModels` can stay empty unless your table uses other row-model features. +To use row pinning, add `rowPinningFeature` to your features. Row pinning does not require a row model factory, so no additional slots are needed on `tableFeatures` unless your table uses other row-model features. ```tsx import { @@ -46,7 +45,6 @@ const features = tableFeatures({ rowPinningFeature }) const table = useTable({ features, - rowModels: {}, columns, data, }) @@ -68,7 +66,6 @@ You can pin rows by default with `initialState.rowPinning`: ```tsx const table = useTable({ features, - rowModels: {}, columns, data, initialState: { @@ -95,7 +92,6 @@ const rowPinning = useSelector(rowPinningAtom) // subscribe wherever it is neede const table = useTable({ features, - rowModels: {}, columns, data, atoms: { @@ -114,7 +110,6 @@ const [rowPinning, setRowPinning] = useState({ const table = useTable({ features, - rowModels: {}, columns, data, state: { @@ -219,7 +214,6 @@ By default, all rows can be pinned. You can disable row pinning for the whole ta ```tsx const table = useTable({ features, - rowModels: {}, columns, data, enableRowPinning: row => row.original.status !== 'archived', @@ -235,7 +229,6 @@ Set `keepPinnedRows` to `false` if pinned rows should only render when they are ```tsx const table = useTable({ features, - rowModels: {}, columns, data, keepPinnedRows: false, diff --git a/docs/framework/react/guide/row-selection.md b/docs/framework/react/guide/row-selection.md index 41b5e6805c..70b08a1e0b 100644 --- a/docs/framework/react/guide/row-selection.md +++ b/docs/framework/react/guide/row-selection.md @@ -17,7 +17,6 @@ const features = tableFeatures({ rowSelectionFeature }) const table = useTable({ features, - rowModels: {}, columns, data, }) @@ -64,7 +63,6 @@ const rowSelection = useSelector(rowSelectionAtom) const table = useTable({ features, - rowModels: {}, //... atoms: { rowSelection: rowSelectionAtom, // selection APIs now update rowSelectionAtom @@ -79,7 +77,6 @@ const [rowSelection, setRowSelection] = useState({}) const table = useTable({ features, - rowModels: {}, //... onRowSelectionChange: setRowSelection, state: { @@ -95,7 +92,6 @@ By default, the row id for each row is simply the `row.index`. If you are using ```ts const table = useTable({ features, - rowModels: {}, //... getRowId: (row) => row.uuid, // use the row's uuid from your database as the row id }) diff --git a/docs/framework/react/guide/sorting.md b/docs/framework/react/guide/sorting.md index 9277a86ea3..c8fb6c5a97 100644 --- a/docs/framework/react/guide/sorting.md +++ b/docs/framework/react/guide/sorting.md @@ -13,13 +13,14 @@ Want to skip to the implementation? Check out these React examples: ```tsx import { useTable, tableFeatures, rowSortingFeature, createSortedRowModel, sortFns } from '@tanstack/react-table' -const features = tableFeatures({ rowSortingFeature }) +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, +}) const table = useTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data, }) @@ -50,7 +51,6 @@ For reactive reads that should re-render your UI, use `table.state.sorting` (sel ```tsx const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, //... @@ -76,7 +76,6 @@ const sorting = useSelector(sortingAtom) const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, //... @@ -93,7 +92,6 @@ const [sorting, setSorting] = useState([]) //... const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, //... @@ -111,7 +109,6 @@ If you do not need to control the sorting state in your own state management or ```tsx const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, //... @@ -148,7 +145,6 @@ const sorting = useSelector(sortingAtom) //... const table = useTable({ features, - rowModels: {}, // no sortedRowModel needed for manual sorting columns, data, manualSorting: true, // use pre-sorted row model instead of sorted row model @@ -164,7 +160,7 @@ Hoisting the sorting state into your own scope (with an external atom or the `st ### Client-Side Sorting -To implement client-side sorting, add the `rowSortingFeature` to your features and the `sortedRowModel` to your row models. Import `createSortedRowModel` and `sortFns` from TanStack Table: +To implement client-side sorting, add the `rowSortingFeature` and the `sortedRowModel` factory to your features. Import `createSortedRowModel` and `sortFns` from TanStack Table: ```tsx import { @@ -175,13 +171,14 @@ import { sortFns, } from '@tanstack/react-table' -const features = tableFeatures({ rowSortingFeature }) +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, +}) const table = useTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data, }) @@ -226,6 +223,19 @@ Every sorting function receives 2 rows and a column ID and are expected to compa | `1` | `a > b` | ```tsx +const myCustomSortFn: SortFn = (rowA, rowB, columnId) => + rowA.original[columnId] > rowB.original[columnId] + ? 1 + : rowA.original[columnId] < rowB.original[columnId] + ? -1 + : 0 + +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns: { ...sortFns, myCustomSortFn }, +}) + const columns = [ { header: () => 'Name', @@ -235,7 +245,7 @@ const columns = [ { header: () => 'Age', accessorKey: 'age', - sortFn: 'myCustomSortFn', // reference a custom sorting function registered with createSortedRowModel + sortFn: 'myCustomSortFn', // reference a custom sorting function registered in features }, { header: () => 'Birthday', @@ -254,33 +264,12 @@ const columns = [ //... const table = useTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel({ - ...sortFns, - myCustomSortFn: (rowA, rowB, columnId) => - rowA.original[columnId] > rowB.original[columnId] - ? 1 - : rowA.original[columnId] < rowB.original[columnId] - ? -1 - : 0, - }), - }, columns, data, }) ``` -> **TypeScript Note:** For `sortFn: 'myCustomSortFn'` string references to typecheck, augment the `SortFns` interface with a `declare module` block: -> -> ```tsx -> declare module '@tanstack/react-table' { -> interface SortFns { -> myCustomSortFn: SortFn -> } -> } -> ``` -> -> Alternatively, skip the registry and the augmentation entirely by passing the function directly to the `sortFn` column option. +> **TypeScript Note:** For `sortFn: 'myCustomSortFn'` string references to typecheck, register the function in the `sortFns` slot on `tableFeatures` (as shown above). Alternatively, skip the registry entirely by passing the function directly to the `sortFn` column option. ### Customize Sorting @@ -306,7 +295,6 @@ const columns = [ //... const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, enableSorting: false, // disable sorting for the entire table @@ -334,7 +322,6 @@ const columns = [ //... const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, sortDescFirst: true, //sort by all columns in descending order first (default is ascending for string columns and descending for number columns) @@ -401,7 +388,6 @@ Once a column is sorted and `enableSortingRemoval` is `false`, toggling the sort ```tsx const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, enableSortingRemoval: false, // disable the ability to remove sorting on columns (sorting can never return to 'none' once applied) @@ -428,7 +414,6 @@ const columns = [ //... const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, enableMultiSort: false, // disable multi-sorting for the entire table @@ -442,7 +427,6 @@ By default, the `Shift` key is used to trigger multi-sorting. You can change thi ```tsx const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, isMultiSortEvent: (e) => true, // normal click triggers multi-sorting @@ -458,7 +442,6 @@ By default, there is no limit to the number of columns that can be sorted at onc ```tsx const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, maxMultiSortColCount: 3, // only allow 3 columns to be sorted at once @@ -472,7 +455,6 @@ By default, the ability to remove multi-sorts is enabled. You can disable this b ```tsx const table = useTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, enableMultiRemove: false, // disable the ability to remove multi-sorts diff --git a/docs/framework/react/guide/table-state.md b/docs/framework/react/guide/table-state.md index ef69af27c6..05333fc22a 100644 --- a/docs/framework/react/guide/table-state.md +++ b/docs/framework/react/guide/table-state.md @@ -47,14 +47,13 @@ For example, if `features` includes `rowPaginationFeature`, TypeScript exposes p const features = tableFeatures({ rowPaginationFeature, rowSortingFeature, + paginatedRowModel: createPaginatedRowModel(), + sortedRowModel: createSortedRowModel(), + sortFns, }) const table = useTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data, }) @@ -104,9 +103,6 @@ You can pass your own selector to make `table.state` contain only the reactive s const table = useTable( { features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, }, @@ -124,7 +120,6 @@ For large tables, you can also opt the parent table component out of table-state const table = useTable( { features, - rowModels: {}, columns, data, }, @@ -144,10 +139,6 @@ Without a `source` prop, `table.Subscribe` subscribes to `table.store` and requi const table = useTable( { features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, }, @@ -291,10 +282,6 @@ If you only need to customize the starting value for some table state, use `init ```tsx const table = useTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, initialState: { @@ -364,7 +351,6 @@ function App() { const table = useTable({ features, - rowModels: {}, columns, data: dataQuery.data?.rows ?? [], rowCount: dataQuery.data?.rowCount, @@ -395,10 +381,6 @@ const [pagination, setPagination] = React.useState({ const table = useTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, state: { @@ -423,9 +405,6 @@ const [sorting, setSorting] = React.useState([]) const table = useTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data, state: { @@ -445,9 +424,6 @@ const [pagination, setPagination] = React.useState({ const table = useTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, state: { diff --git a/docs/framework/react/guide/virtualization.md b/docs/framework/react/guide/virtualization.md index fdd7c71bdf..815e2a8820 100644 --- a/docs/framework/react/guide/virtualization.md +++ b/docs/framework/react/guide/virtualization.md @@ -61,13 +61,12 @@ import { useVirtualizer } from '@tanstack/react-virtual' const features = tableFeatures({ columnSizingFeature, rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, }) const table = useTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data, }) diff --git a/docs/framework/react/quick-start.md b/docs/framework/react/quick-start.md index ff3cdca03f..6a33085405 100644 --- a/docs/framework/react/quick-start.md +++ b/docs/framework/react/quick-start.md @@ -61,7 +61,6 @@ export function PersonTable() { const table = useTable({ key: 'person-table', // needed for devtools, omit if you don't want to use the devtools features, - rowModels: {}, // the core row model is included by default columns, data, }) @@ -101,7 +100,7 @@ export function PersonTable() { A few things to note: - `tableFeatures({})` declares which optional features the table uses. Registering only what you need keeps bundles small and gives TypeScript accurate types for the table instance. -- `rowModels: {}` is fine for a basic table because the core row model is always included. Feature row models (sorting, filtering, pagination) are registered here when you need them. +- The core row model is always included automatically. Feature row models (sorting, filtering, pagination) are registered as slots on the features object when you need them. - `table.FlexRender` renders the `header`, `cell`, and `footer` definitions from your columns, whether they are plain values or React components. - The `key` option is optional unless you use the [TanStack Table Devtools](../../devtools). The devtools identify tables by `key`, and you register a table by calling `useTanStackTableDevtools(table)` from `@tanstack/react-table-devtools`. @@ -109,7 +108,7 @@ See the full [Basic useTable example](./examples/basic-use-table) for a runnable ## Add a Feature: Sorting -Features are opt-in in v9. To make columns sortable, register `rowSortingFeature` in `tableFeatures`, register a sorted row model under `rowModels`, and wire the header click handler. +Features are opt-in in v9. To make columns sortable, register `rowSortingFeature` and the sorted row model in `tableFeatures`, then wire the header click handler. ```tsx import { @@ -122,15 +121,14 @@ import { const features = tableFeatures({ rowSortingFeature, // enables sorting APIs and state + sortedRowModel: createSortedRowModel(), // client-side sorting + sortFns, }) export function PersonTable() { const table = useTable({ key: 'person-table', features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), // client-side sorting - }, columns, data, }) @@ -167,7 +165,7 @@ export function PersonTable() { } ``` -Clicking a header now toggles between ascending, descending, and unsorted. Every other feature follows this same pattern: register the feature, register its row model if it has one, and use the APIs it adds to the table, columns, and rows. See the [Sorting Guide](./guide/sorting) and the [Sorting example](./examples/sorting) for custom sort functions, multi-sorting, and per-column options. +Clicking a header now toggles between ascending, descending, and unsorted. Every other feature follows this same pattern: register the feature and its row model factory (if it has one) in `tableFeatures`, then use the APIs it adds to the table, columns, and rows. See the [Sorting Guide](./guide/sorting) and the [Sorting example](./examples/sorting) for custom sort functions, multi-sorting, and per-column options. ## Where to Go Next @@ -178,12 +176,13 @@ Clicking a header now toggles between ascending, descending, and unsorted. Every **Composable tables.** When multiple tables in your app share features, row models, and component conventions, define them once with `createTableHook`: ```tsx -const features = tableFeatures({ rowSortingFeature }) - -const { useAppTable, createAppColumnHelper } = createTableHook({ - features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, }) + +const { useAppTable, createAppColumnHelper } = createTableHook({ features }) ``` See the [Composable Tables Guide](./guide/composable-tables) for the full pattern, including pre-bound cell and header components. diff --git a/docs/framework/react/reference/index/functions/createTableHook.md b/docs/framework/react/reference/index/functions/createTableHook.md index 0dfc0ee4f6..3810f62ba5 100644 --- a/docs/framework/react/reference/index/functions/createTableHook.md +++ b/docs/framework/react/reference/index/functions/createTableHook.md @@ -116,7 +116,7 @@ TFeatures is already known from the createTableHook call; TData is inferred from ##### tableOptions -`Omit`\<`TableOptions`\<`TFeatures`, `TData`\>, `"features"` \| `"rowModels"`\> +`Omit`\<`TableOptions`\<`TFeatures`, `TData`\>, `"features"`\> ##### selector? @@ -256,12 +256,12 @@ export const { rowPaginationFeature, rowSortingFeature, columnFilteringFeature, - }), - rowModels: { paginatedRowModel: createPaginatedRowModel(), - sortedRowModel: createSortedRowModel(sortFns), - filteredRowModel: createFilteredRowModel(filterFns), - }, + sortedRowModel: createSortedRowModel(), + filteredRowModel: createFilteredRowModel(), + sortFns, + filterFns, + }), tableComponents: { PaginationControls, RowCount }, cellComponents: { TextCell, NumberCell }, headerComponents: { SortIndicator, ColumnFilter }, diff --git a/docs/framework/react/reference/index/functions/useTable.md b/docs/framework/react/reference/index/functions/useTable.md index d28dfafc1b..7c8a4a1bd6 100644 --- a/docs/framework/react/reference/index/functions/useTable.md +++ b/docs/framework/react/reference/index/functions/useTable.md @@ -9,7 +9,7 @@ title: useTable function useTable(tableOptions, selector?): ReactTable; ``` -Defined in: [useTable.ts:142](https://github.com/TanStack/table/blob/main/packages/react-table/src/useTable.ts#L142) +Defined in: [useTable.ts:141](https://github.com/TanStack/table/blob/main/packages/react-table/src/useTable.ts#L141) Creates a React table instance backed by TanStack Store atoms. @@ -53,7 +53,6 @@ subscriptions. const table = useTable( { features, - rowModels: {}, columns, data, }, diff --git a/docs/framework/react/reference/legacy/functions/getFilteredRowModel.md b/docs/framework/react/reference/legacy/functions/getFilteredRowModel.md index 4e53c9bdcc..8d376fae96 100644 --- a/docs/framework/react/reference/legacy/functions/getFilteredRowModel.md +++ b/docs/framework/react/reference/legacy/functions/getFilteredRowModel.md @@ -23,7 +23,7 @@ Defined in: [useLegacyTable.ts:54](https://github.com/TanStack/table/blob/main/p ## Deprecated -Use `createFilteredRowModel(filterFns)` with the new `useTable` hook instead. +Use `createFilteredRowModel()` in the `filteredRowModel` feature slot with the new `useTable` hook instead. This is a stub function for v8 API compatibility with `useLegacyTable`. It acts as a marker to enable the filtered row model. diff --git a/docs/framework/react/reference/legacy/functions/getGroupedRowModel.md b/docs/framework/react/reference/legacy/functions/getGroupedRowModel.md index b6723147c5..785db54872 100644 --- a/docs/framework/react/reference/legacy/functions/getGroupedRowModel.md +++ b/docs/framework/react/reference/legacy/functions/getGroupedRowModel.md @@ -23,7 +23,7 @@ Defined in: [useLegacyTable.ts:102](https://github.com/TanStack/table/blob/main/ ## Deprecated -Use `createGroupedRowModel(aggregationFns)` with the new `useTable` hook instead. +Use `createGroupedRowModel()` in the `groupedRowModel` feature slot with the new `useTable` hook instead. This is a stub function for v8 API compatibility with `useLegacyTable`. It acts as a marker to enable the grouped row model. diff --git a/docs/framework/react/reference/legacy/functions/getSortedRowModel.md b/docs/framework/react/reference/legacy/functions/getSortedRowModel.md index c60dd5323d..84a277e260 100644 --- a/docs/framework/react/reference/legacy/functions/getSortedRowModel.md +++ b/docs/framework/react/reference/legacy/functions/getSortedRowModel.md @@ -23,7 +23,7 @@ Defined in: [useLegacyTable.ts:66](https://github.com/TanStack/table/blob/main/p ## Deprecated -Use `createSortedRowModel(sortFns)` with the new `useTable` hook instead. +Use `createSortedRowModel()` in the `sortedRowModel` feature slot with the new `useTable` hook instead. This is a stub function for v8 API compatibility with `useLegacyTable`. It acts as a marker to enable the sorted row model. diff --git a/docs/framework/react/reference/legacy/functions/useLegacyTable.md b/docs/framework/react/reference/legacy/functions/useLegacyTable.md index c3baa57a9c..e8bc8c3e51 100644 --- a/docs/framework/react/reference/legacy/functions/useLegacyTable.md +++ b/docs/framework/react/reference/legacy/functions/useLegacyTable.md @@ -35,7 +35,7 @@ A table instance with the full state subscribed and a `getState()` method This hook is provided as a compatibility layer for migrating from TanStack Table v8. -Use the new `useTable` hook instead with explicit `features` and `rowModels`: +Use the new `useTable` hook instead with an explicit `features` option: ```tsx // New v9 API @@ -43,15 +43,15 @@ const features = tableFeatures({ columnFilteringFeature, rowSortingFeature, rowPaginationFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), + paginatedRowModel: createPaginatedRowModel(), + filterFns, + sortFns, }) const table = useTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, }) @@ -59,6 +59,6 @@ const table = useTable({ Key differences from v8: - Features are tree-shakeable - only import what you use -- Row models are explicitly passed via `rowModels` +- Row models and fn registries are explicitly passed on the `features` option - Use `table.Subscribe` for fine-grained re-renders - State is accessed via `table.state` after selecting with the 2nd argument diff --git a/docs/framework/react/reference/legacy/interfaces/LegacyRowModelOptions.md b/docs/framework/react/reference/legacy/interfaces/LegacyRowModelOptions.md index 250573f1c2..cd6bc76c37 100644 --- a/docs/framework/react/reference/legacy/interfaces/LegacyRowModelOptions.md +++ b/docs/framework/react/reference/legacy/interfaces/LegacyRowModelOptions.md @@ -29,7 +29,7 @@ Additional aggregation functions to apply to the table. #### Deprecated -Use `rowModels.groupedRowModel` with `createGroupedRowModel(aggregationFns)` instead. +Use the `groupedRowModel`/`aggregationFns` slots on the `features` option with `createGroupedRowModel()` instead. *** @@ -45,7 +45,7 @@ Additional filter functions to apply to the table. #### Deprecated -Use `rowModels.filteredRowModel` with `createFilteredRowModel(filterFns)` instead. +Use the `filteredRowModel`/`filterFns` slots on the `features` option with `createFilteredRowModel()` instead. *** @@ -77,7 +77,7 @@ Returns the expanded row model for the table. #### Deprecated -Use `rowModels.expandedRowModel` with `createExpandedRowModel()` instead. +Use the `expandedRowModel` slot on the `features` option with `createExpandedRowModel()` instead. *** @@ -93,7 +93,7 @@ Returns the faceted min/max values for a column. #### Deprecated -Use `rowModels.facetedMinMaxValues` with `createFacetedMinMaxValues()` instead. +Use the `facetedMinMaxValues` slot on the `features` option with `createFacetedMinMaxValues()` instead. *** @@ -109,7 +109,7 @@ Returns the faceted row model for a column. #### Deprecated -Use `rowModels.facetedRowModel` with `createFacetedRowModel()` instead. +Use the `facetedRowModel` slot on the `features` option with `createFacetedRowModel()` instead. *** @@ -125,7 +125,7 @@ Returns the faceted unique values for a column. #### Deprecated -Use `rowModels.facetedUniqueValues` with `createFacetedUniqueValues()` instead. +Use the `facetedUniqueValues` slot on the `features` option with `createFacetedUniqueValues()` instead. *** @@ -141,7 +141,7 @@ Returns the filtered row model for the table. #### Deprecated -Use `rowModels.filteredRowModel` with `createFilteredRowModel(filterFns)` instead. +Use the `filteredRowModel`/`filterFns` slots on the `features` option with `createFilteredRowModel()` instead. *** @@ -157,7 +157,7 @@ Returns the grouped row model for the table. #### Deprecated -Use `rowModels.groupedRowModel` with `createGroupedRowModel(aggregationFns)` instead. +Use the `groupedRowModel`/`aggregationFns` slots on the `features` option with `createGroupedRowModel()` instead. *** @@ -173,7 +173,7 @@ Returns the paginated row model for the table. #### Deprecated -Use `rowModels.paginatedRowModel` with `createPaginatedRowModel()` instead. +Use the `paginatedRowModel` slot on the `features` option with `createPaginatedRowModel()` instead. *** @@ -189,7 +189,7 @@ Returns the sorted row model for the table. #### Deprecated -Use `rowModels.sortedRowModel` with `createSortedRowModel(sortFns)` instead. +Use the `sortedRowModel`/`sortFns` slots on the `features` option with `createSortedRowModel()` instead. *** @@ -205,4 +205,4 @@ Additional sort functions to apply to the table. #### Deprecated -Use `rowModels.sortedRowModel` with `createSortedRowModel(sortFns)` instead. +Use the `sortedRowModel`/`sortFns` slots on the `features` option with `createSortedRowModel()` instead. diff --git a/docs/framework/react/reference/legacy/type-aliases/LegacyTableOptions.md b/docs/framework/react/reference/legacy/type-aliases/LegacyTableOptions.md index 4bab6eccb1..0a0d56160f 100644 --- a/docs/framework/react/reference/legacy/type-aliases/LegacyTableOptions.md +++ b/docs/framework/react/reference/legacy/type-aliases/LegacyTableOptions.md @@ -6,14 +6,14 @@ title: LegacyTableOptions # ~~Type Alias: LegacyTableOptions\~~ ```ts -type LegacyTableOptions = Omit, "features" | "rowModels"> & LegacyRowModelOptions; +type LegacyTableOptions = Omit, "features"> & LegacyRowModelOptions; ``` Defined in: [useLegacyTable.ts:265](https://github.com/TanStack/table/blob/main/packages/react-table/src/useLegacyTable.ts#L265) Legacy v8-style table options that work with useLegacyTable. -This type omits `features` and `rowModels` and instead accepts the v8-style +This type omits `features` and instead accepts the v8-style `get*RowModel` function options. ## Type Parameters @@ -24,4 +24,4 @@ This type omits `features` and `rowModels` and instead accepts the v8-style ## Deprecated -This is a compatibility layer for migrating from v8. Use `useTable` with explicit `features` and `rowModels` instead. +This is a compatibility layer for migrating from v8. Use `useTable` with an explicit `features` option instead. diff --git a/docs/framework/solid/guide/column-faceting.md b/docs/framework/solid/guide/column-faceting.md index 380b03fbb1..bea0147f6d 100644 --- a/docs/framework/solid/guide/column-faceting.md +++ b/docs/framework/solid/guide/column-faceting.md @@ -15,16 +15,18 @@ Use getters for reactive inputs such as `data` when passing Solid signals to `cr ```tsx import { createTable, tableFeatures, columnFacetingFeature, columnFilteringFeature, createFacetedRowModel, createFacetedUniqueValues, createFacetedMinMaxValues, createFilteredRowModel, filterFns } from '@tanstack/solid-table' -const features = tableFeatures({ columnFacetingFeature, columnFilteringFeature }) +const features = tableFeatures({ + columnFacetingFeature, + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + facetedRowModel: createFacetedRowModel(), + facetedUniqueValues: createFacetedUniqueValues(), + facetedMinMaxValues: createFacetedMinMaxValues(), + filterFns, +}) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - facetedRowModel: createFacetedRowModel(), - facetedUniqueValues: createFacetedUniqueValues(), - facetedMinMaxValues: createFacetedMinMaxValues(), - }, columns, get data() { return data() @@ -38,7 +40,7 @@ Faceting is a feature that generates lists of values from your table's data, eit ### Column Faceting Row Models -In order to use any of the column faceting features, add the `columnFacetingFeature` to your features and the appropriate faceted row models to `rowModels`. Faceting exists to power filter UIs, so in practice you will also register the `columnFilteringFeature` and a `filteredRowModel`. Without a filtered row model, the faceted row models fall back to the pre-filtered rows and the facet values will not react to other columns' filters. +In order to use any of the column faceting features, add the `columnFacetingFeature` and the appropriate faceted row model factories to your features. Faceting exists to power filter UIs, so in practice you will also register the `columnFilteringFeature` and a `filteredRowModel`. Without a filtered row model, the faceted row models fall back to the pre-filtered rows and the facet values will not react to other columns' filters. ```ts import { @@ -53,16 +55,18 @@ import { filterFns, } from '@tanstack/solid-table' -const features = tableFeatures({ columnFacetingFeature, columnFilteringFeature }) +const features = tableFeatures({ + columnFacetingFeature, + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), // facet values react to other columns' filters + facetedRowModel: createFacetedRowModel(), // required for faceting (other faceted row models depend on this) + facetedMinMaxValues: createFacetedMinMaxValues(), // if you need min/max values + facetedUniqueValues: createFacetedUniqueValues(), // if you need a list of unique values + filterFns, +}) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), // facet values react to other columns' filters - facetedRowModel: createFacetedRowModel(), // required for faceting (other faceted row models depend on this) - facetedMinMaxValues: createFacetedMinMaxValues(), // if you need min/max values - facetedUniqueValues: createFacetedUniqueValues(), // if you need a list of unique values - }, columns, data, }) @@ -112,26 +116,28 @@ const [min, max] = table.getGlobalFacetedMinMaxValues() ?? [0, 1]; ### Custom (Server-Side) Faceting -Instead of using the built-in client-side faceting features, you can implement your own faceting logic on the server-side and pass the faceted values to the client-side. Supply custom `rowModels.facetedUniqueValues` and `rowModels.facetedMinMaxValues` factories. Each factory receives the table and a column ID and returns a thunk that resolves the faceted values. The column instance APIs (`column.getFacetedUniqueValues()` and `column.getFacetedMinMaxValues()`) will then return your server-provided values. +Instead of using the built-in client-side faceting features, you can implement your own faceting logic on the server-side and pass the faceted values to the client-side. Supply custom `facetedUniqueValues` and `facetedMinMaxValues` factories in `tableFeatures`. Each factory receives the table and a column ID and returns a thunk that resolves the faceted values. The column instance APIs (`column.getFacetedUniqueValues()` and `column.getFacetedMinMaxValues()`) will then return your server-provided values. ```ts const facetingQuery = useQuery( //... ) +const features = tableFeatures({ + columnFacetingFeature, + facetedUniqueValues: (_table, columnId) => () => { + const uniqueValueMap = new Map() + //... populate from facetingQuery data for this columnId + return uniqueValueMap + }, + facetedMinMaxValues: (_table, columnId) => () => { + //... read from facetingQuery data for this columnId + return [min, max] + }, +}) + const table = createTable({ features, - rowModels: { - facetedUniqueValues: (_table, columnId) => () => { - const uniqueValueMap = new Map() - //... populate from facetingQuery data for this columnId - return uniqueValueMap - }, - facetedMinMaxValues: (_table, columnId) => () => { - //... read from facetingQuery data for this columnId - return [min, max] - }, - }, columns, data, //... diff --git a/docs/framework/solid/guide/column-filtering.md b/docs/framework/solid/guide/column-filtering.md index 1ac99e17ed..9c44f7cf47 100644 --- a/docs/framework/solid/guide/column-filtering.md +++ b/docs/framework/solid/guide/column-filtering.md @@ -17,13 +17,14 @@ Use getters for reactive inputs such as `data` when passing Solid signals to `cr ```tsx import { createTable, tableFeatures, columnFilteringFeature, createFilteredRowModel, filterFns } from '@tanstack/solid-table' -const features = tableFeatures({ columnFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, columns, get data() { return data() @@ -59,14 +60,13 @@ If you're not sure, you can always start with client-side filtering and paginati If you have decided that you need to implement server-side filtering instead of using the built-in client-side filtering, here's how you do that. -No `filteredRowModel` is needed for manual server-side filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to `rowModels`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. +No `filteredRowModel` is needed for manual server-side filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to `tableFeatures`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. ```tsx const features = tableFeatures({ columnFilteringFeature }) const table = createTable({ features, - rowModels: {}, // no filteredRowModel needed for manual server-side filtering data, columns, manualFiltering: true, @@ -77,7 +77,7 @@ const table = createTable({ ### Client-Side Filtering -If you are using the built-in client-side filtering features, add the `columnFilteringFeature` to your features and the `filteredRowModel` to your row models. Import `createFilteredRowModel` and `filterFns` from TanStack Table: +If you are using the built-in client-side filtering features, add the `columnFilteringFeature` and the `filteredRowModel` factory to your features. Import `createFilteredRowModel` and `filterFns` from TanStack Table: ```tsx import { @@ -88,13 +88,14 @@ import { filterFns, } from '@tanstack/solid-table' -const features = tableFeatures({ columnFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, data, columns, }) @@ -123,7 +124,6 @@ In Solid, the table's state atoms are backed by Solid signals, so `table.atoms.c ```tsx const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, //... @@ -148,7 +148,6 @@ const columnFilters = useSelector(columnFiltersAtom) const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, //... @@ -165,7 +164,6 @@ const [columnFilters, setColumnFilters] = createSignal([]) //... const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, //... @@ -185,7 +183,6 @@ If you do not need to control the column filter state in your own state manageme ```tsx const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, //... @@ -273,33 +270,26 @@ const columns = [ } ] //... +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns: { + ...filterFns, + myCustomFilterFn: (row, columnId, filterValue) => { + return // true or false based on your custom logic + }, + startsWith: startsWithFilterFn, // defined elsewhere + }, +}) + const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel({ - ...filterFns, - myCustomFilterFn: (row, columnId, filterValue) => { - return // true or false based on your custom logic - }, - startsWith: startsWithFilterFn, // defined elsewhere - }), - }, columns, data, }) ``` -> **TypeScript Note:** For `filterFn: 'myCustomFilterFn'` string references to typecheck, augment the `FilterFns` interface with a `declare module` block: -> -> ```tsx -> declare module '@tanstack/solid-table' { -> interface FilterFns { -> myCustomFilterFn: FilterFn -> } -> } -> ``` -> -> Alternatively, skip the registry and the augmentation entirely by passing the function directly to the `filterFn` column option. See the [Fuzzy Search example](../examples/filters-fuzzy) for a complete registration with module augmentation. +> **TypeScript Note:** For `filterFn: 'myCustomFilterFn'` string references to typecheck, register the function in the `filterFns` slot on `tableFeatures` (as shown above). The slot is the registry; no `declare module` augmentation is needed. Alternatively, skip the registry entirely by passing the function directly to the `filterFn` column option. ##### Customize Filter Function Behavior @@ -351,7 +341,6 @@ const columns = [ //... const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, enableColumnFilters: false, // disable column filtering for all columns @@ -369,14 +358,16 @@ By default, filtering is done from parent rows down, so if a parent row is filte However, if you want to allow sub-rows to be filtered and searched through, regardless of whether the parent row is filtered out, you can set the `filterFromLeafRows` table option to `true`. Setting this option to `true` will cause filtering to be done from leaf rows up, which means parent rows will be included so long as one of their child or grand-child rows is also included. ```tsx -const features = tableFeatures({ columnFilteringFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnFilteringFeature, + rowExpandingFeature, + filteredRowModel: createFilteredRowModel(), + expandedRowModel: createExpandedRowModel(), + filterFns, +}) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - expandedRowModel: createExpandedRowModel(), - }, columns, data, filterFromLeafRows: true, // filter and search through sub-rows @@ -390,14 +381,16 @@ By default, filtering is done for all rows in a tree, no matter if they are root Use `maxLeafRowFilterDepth: 0` if you want to preserve a parent row's sub-rows from being filtered out while the parent row is passing the filter. ```tsx -const features = tableFeatures({ columnFilteringFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnFilteringFeature, + rowExpandingFeature, + filteredRowModel: createFilteredRowModel(), + expandedRowModel: createExpandedRowModel(), + filterFns, +}) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - expandedRowModel: createExpandedRowModel(), - }, columns, data, maxLeafRowFilterDepth: 0, // only filter root level parent rows out diff --git a/docs/framework/solid/guide/column-ordering.md b/docs/framework/solid/guide/column-ordering.md index c9dbde19a5..86c2d205b3 100644 --- a/docs/framework/solid/guide/column-ordering.md +++ b/docs/framework/solid/guide/column-ordering.md @@ -19,7 +19,6 @@ const features = tableFeatures({ columnOrderingFeature }) const table = createTable({ features, - rowModels: {}, columns, get data() { return data() @@ -54,7 +53,6 @@ const features = tableFeatures({ columnOrderingFeature }) const table = createTable({ features, - rowModels: {}, //... initialState: { columnOrder: ['columnId1', 'columnId2', 'columnId3'], @@ -88,7 +86,6 @@ const columnOrder = useSelector(columnOrderAtom) // subscribe wherever it is nee const table = createTable({ features, - rowModels: {}, //... atoms: { columnOrder: columnOrderAtom, @@ -106,7 +103,6 @@ const [columnOrder, setColumnOrder] = createSignal(['columnId1 //... const table = createTable({ features, - rowModels: {}, //... state: { get columnOrder() { diff --git a/docs/framework/solid/guide/column-pinning.md b/docs/framework/solid/guide/column-pinning.md index 68073a82fa..c5defbdb03 100644 --- a/docs/framework/solid/guide/column-pinning.md +++ b/docs/framework/solid/guide/column-pinning.md @@ -21,7 +21,6 @@ const features = tableFeatures({ columnPinningFeature }) const table = createTable({ features, - rowModels: {}, columns, get data() { return data() @@ -65,7 +64,6 @@ const columnPinning = useSelector(columnPinningAtom) // subscribe wherever it is const table = createTable({ features, - rowModels: {}, //... atoms: { columnPinning: columnPinningAtom, @@ -84,7 +82,6 @@ const [columnPinning, setColumnPinning] = createSignal({ const table = createTable({ features, - rowModels: {}, //... state: { get columnPinning() { @@ -104,7 +101,6 @@ A very common use case is to pin some columns by default. You can do this by eit ```tsx const table = createTable({ features, - rowModels: {}, //... initialState: { columnPinning: { diff --git a/docs/framework/solid/guide/column-resizing.md b/docs/framework/solid/guide/column-resizing.md index d70a5e2a9a..d8c188072c 100644 --- a/docs/framework/solid/guide/column-resizing.md +++ b/docs/framework/solid/guide/column-resizing.md @@ -20,7 +20,6 @@ const features = tableFeatures({ columnResizingFeature }) const table = createTable({ features, - rowModels: {}, columns, get data() { return data() @@ -62,7 +61,6 @@ const columns = [ const table = createTable({ features, - rowModels: {}, columns, data, }) @@ -181,7 +179,6 @@ const columnResizing = useSelector(columnResizingAtom) // subscribe wherever it const table = createTable({ features, - rowModels: {}, columns, data, atoms: { @@ -204,7 +201,6 @@ const [columnResizing, setColumnResizing] = createSignal({ const table = createTable({ features, - rowModels: {}, columns, data, state: { diff --git a/docs/framework/solid/guide/column-sizing.md b/docs/framework/solid/guide/column-sizing.md index 27b5b4c5b1..7c86f830d9 100644 --- a/docs/framework/solid/guide/column-sizing.md +++ b/docs/framework/solid/guide/column-sizing.md @@ -19,7 +19,6 @@ const features = tableFeatures({ columnSizingFeature }) const table = createTable({ features, - rowModels: {}, columns, get data() { return data() @@ -60,7 +59,6 @@ const columns = [ const table = createTable({ features, - rowModels: {}, defaultColumn: { size: 200, // starting column size minSize: 50, // enforced during column resizing @@ -142,7 +140,6 @@ const columnSizing = useSelector(columnSizingAtom) // subscribe wherever it is n const table = createTable({ features, - rowModels: {}, columns, data, atoms: { @@ -160,7 +157,6 @@ const [columnSizing, setColumnSizing] = createSignal({}) const table = createTable({ features, - rowModels: {}, columns, data, state: { diff --git a/docs/framework/solid/guide/column-visibility.md b/docs/framework/solid/guide/column-visibility.md index d2b2080e5d..a892d2a8fa 100644 --- a/docs/framework/solid/guide/column-visibility.md +++ b/docs/framework/solid/guide/column-visibility.md @@ -19,7 +19,6 @@ const features = tableFeatures({ columnVisibilityFeature }) const table = createTable({ features, - rowModels: {}, columns, get data() { return data() @@ -54,7 +53,6 @@ const columnVisibility = useSelector(columnVisibilityAtom) // subscribe wherever const table = createTable({ features, - rowModels: {}, //... atoms: { columnVisibility: columnVisibilityAtom, @@ -75,7 +73,6 @@ const [columnVisibility, setColumnVisibility] = createSignal row.id, tableComponents: { PaginationControls, diff --git a/docs/framework/solid/guide/custom-features.md b/docs/framework/solid/guide/custom-features.md index 959ede150e..c62fb77387 100644 --- a/docs/framework/solid/guide/custom-features.md +++ b/docs/framework/solid/guide/custom-features.md @@ -264,7 +264,6 @@ const features = tableFeatures({ densityPlugin }) const table = createTable({ features, - rowModels: {}, columns, data, //.. @@ -282,7 +281,6 @@ const [density, setDensity] = createSignal('md') const table = createTable({ features, - rowModels: {}, columns, data, //... diff --git a/docs/framework/solid/guide/expanding.md b/docs/framework/solid/guide/expanding.md index 2cd5b8e18b..c2deea1e9e 100644 --- a/docs/framework/solid/guide/expanding.md +++ b/docs/framework/solid/guide/expanding.md @@ -15,13 +15,13 @@ Use getters for reactive inputs such as `data` when passing Solid signals to `cr ```tsx import { createTable, tableFeatures, rowExpandingFeature, createExpandedRowModel } from '@tanstack/solid-table' -const features = tableFeatures({ rowExpandingFeature }) +const features = tableFeatures({ + rowExpandingFeature, + expandedRowModel: createExpandedRowModel(), +}) const table = createTable({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, columns, get data() { return data() @@ -42,7 +42,7 @@ There are multiple use cases for expanding features in TanStack Table that will ### Enable Client-Side Expanding -To use the client-side expanding features, add the `rowExpandingFeature` to your features and the `expandedRowModel` to your row models: +To use the client-side expanding features, add the `rowExpandingFeature` and the `expandedRowModel` factory to your features: ```ts import { @@ -52,13 +52,13 @@ import { createExpandedRowModel, } from '@tanstack/solid-table' -const features = tableFeatures({ rowExpandingFeature }) +const features = tableFeatures({ + rowExpandingFeature, + expandedRowModel: createExpandedRowModel(), +}) const table = createTable({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, // other options... }) ``` @@ -103,9 +103,6 @@ Then you can use the getSubRows function to return the children array in each ro ```ts const table = createTable({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, getSubRows: (row) => row.children, // return the children array as sub-rows // other options... }) @@ -158,7 +155,6 @@ const expanded = useSelector(expandedAtom) const table = createTable({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, // other options... atoms: { expanded: expandedAtom, // expanding APIs now update expandedAtom @@ -173,7 +169,6 @@ const [expanded, setExpanded] = createSignal({}) const table = createTable({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, // other options... state: { get expanded() { @@ -252,15 +247,17 @@ Use `table.setExpanded` to update the expanded state directly. `table.resetExpan By default, the filtering process starts from the parent rows and moves downwards. This means if a parent row is excluded by the filter, all its child rows will also be excluded. However, you can change this behavior by using the `filterFromLeafRows` option. When this option is enabled, the filtering process starts from the leaf (child) rows and moves upwards. This ensures that a parent row will be included in the filtered results as long as at least one of its child or grandchild rows meets the filter criteria. Additionally, you can control how deep into the child hierarchy the filter process goes by using the `maxLeafRowFilterDepth` option. This option allows you to specify the maximum depth of child rows that the filter should consider. ```ts -const features = tableFeatures({ columnFilteringFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnFilteringFeature, + rowExpandingFeature, + filteredRowModel: createFilteredRowModel(), + expandedRowModel: createExpandedRowModel(), + filterFns, +}) //... const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - expandedRowModel: createExpandedRowModel(), - }, getSubRows: (row) => row.subRows, filterFromLeafRows: true, // search through the expanded rows maxLeafRowFilterDepth: 1, // limit the depth of the expanded rows that are searched @@ -275,7 +272,6 @@ By default, expanded rows are paginated along with the rest of the table (which ```ts const table = createTable({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, // other options... paginateExpandedRows: false, }) @@ -298,7 +294,6 @@ A common reason to set `autoResetExpanded: false` is editing data while viewing ```ts const table = createTable({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, // other options... autoResetExpanded: false, // keep expanded state when data changes // autoResetAll: false, // or turn off all auto resets at once @@ -314,7 +309,6 @@ const features = tableFeatures({ rowExpandingFeature }) const table = createTable({ features, - rowModels: {}, // no expandedRowModel needed for manual expanding // other options... manualExpanding: true, }) diff --git a/docs/framework/solid/guide/fuzzy-filtering.md b/docs/framework/solid/guide/fuzzy-filtering.md index 30529ddf53..ae65c8481d 100644 --- a/docs/framework/solid/guide/fuzzy-filtering.md +++ b/docs/framework/solid/guide/fuzzy-filtering.md @@ -19,14 +19,14 @@ const features = tableFeatures({ columnFilteringFeature, globalFilteringFeature, rowSortingFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), + filterFns, + sortFns, }) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - sortedRowModel: createSortedRowModel(sortFns), - }, columns, get data() { return data() @@ -54,9 +54,12 @@ Here's an example of a custom fuzzy filter function: ```typescript import { rankItem } from '@tanstack/match-sorter-utils' import type { RankingInfo } from '@tanstack/match-sorter-utils' -import type { FilterFn, RowData } from '@tanstack/solid-table' +import type { FilterFn, RowData, TableFeatures } from '@tanstack/solid-table' -const fuzzyFilter: FilterFn = ( +interface FuzzyFilterMeta { itemRank?: RankingInfo } +type FuzzyFeatures = TableFeatures & { filterMeta: FuzzyFilterMeta } + +const fuzzyFilter: FilterFn = ( row, columnId, value, @@ -75,27 +78,41 @@ const fuzzyFilter: FilterFn = ( In this function, we're using the `rankItem` function from the `@tanstack/match-sorter-utils` library to rank the item. We then store the ranking information in the filter meta of the row (the `addMeta` callback is optional, so call it with optional chaining), and return whether the item passed the ranking criteria. -To reference this filter function by the string name `'fuzzy'` (and to type the stored filter meta), augment the `FilterFns` and `FilterMeta` interfaces with a `declare module` block: +To reference this filter function by the string name `'fuzzy'` (and to type the stored filter meta), register it in the `filterFns` slot on `tableFeatures` and declare a `filterMeta` slot for the meta type: ```typescript -declare module '@tanstack/solid-table' { - // add the fuzzy filter to the filterFns registry types - interface FilterFns { - fuzzy: FilterFn - } - interface FilterMeta { - itemRank?: RankingInfo - } +import { metaHelper } from '@tanstack/solid-table' +import type { FilterFn, RowData, TableFeatures } from '@tanstack/solid-table' + +interface FuzzyFilterMeta { itemRank?: RankingInfo } +type FuzzyFeatures = TableFeatures & { filterMeta: FuzzyFilterMeta } + +const fuzzyFilter: FilterFn = (row, columnId, value, addMeta) => { + const itemRank = rankItem(row.getValue(columnId), value) + addMeta?.({ itemRank }) + return itemRank.passed } + +const features = tableFeatures({ + columnFilteringFeature, + globalFilteringFeature, + rowSortingFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + sortFns, + filterMeta: metaHelper(), +}) ``` ### Using Fuzzy Filtering with Global Filtering -To use fuzzy filtering with global filtering, register the fuzzy filter function in the registry passed to `createFilteredRowModel` and reference it in the `globalFilterFn` option of the table: +To use fuzzy filtering with global filtering, register the fuzzy filter function in the `filterFns` slot on `tableFeatures` and reference it in the `globalFilterFn` option of the table: ```typescript import { createTable, + metaHelper, tableFeatures, columnFilteringFeature, globalFilteringFeature, @@ -110,17 +127,15 @@ const features = tableFeatures({ columnFilteringFeature, globalFilteringFeature, rowSortingFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), // needed if you want sorting with fuzzy rank + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + sortFns, + filterMeta: metaHelper(), }) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel({ - ...filterFns, - fuzzy: fuzzyFilter, - }), - sortedRowModel: createSortedRowModel(sortFns), // needed if you want sorting with fuzzy rank - }, columns, data, globalFilterFn: 'fuzzy', @@ -155,7 +170,7 @@ import { compareItems } from '@tanstack/match-sorter-utils' import { sortFns } from '@tanstack/solid-table' import type { SortFn } from '@tanstack/solid-table' -const fuzzySort: SortFn = (rowA, rowB, columnId) => { +const fuzzySort: SortFn = (rowA, rowB, columnId) => { let dir = 0 // Only sort by rank if the column has ranking information @@ -186,4 +201,4 @@ You can then pass this sorting function directly to the `sortFn` option of the c } ``` -> **Note:** Unlike `filterFn: 'fuzzy'` above, `fuzzySort` is passed as a function rather than a string. A string reference like `sortFn: 'fuzzySort'` would only work if you also registered the function in the registry passed to `createSortedRowModel` (e.g. `createSortedRowModel({ ...sortFns, fuzzySort })`) and augmented the `SortFns` interface the same way as `FilterFns`. Passing the function directly skips both steps. +> **Note:** Unlike `filterFn: 'fuzzy'` above, `fuzzySort` is passed as a function rather than a string. A string reference like `sortFn: 'fuzzySort'` would only work if you also registered the function in the `sortFns` slot on `tableFeatures` (e.g. `sortFns: { ...sortFns, fuzzySort }`). Passing the function directly skips that step. diff --git a/docs/framework/solid/guide/global-filtering.md b/docs/framework/solid/guide/global-filtering.md index d0fd2d6b4e..597ba207da 100644 --- a/docs/framework/solid/guide/global-filtering.md +++ b/docs/framework/solid/guide/global-filtering.md @@ -14,15 +14,17 @@ Use getters for reactive inputs such as `data` when passing Solid signals to `cr ### Solid Setup ```tsx -import { createTable, tableFeatures, globalFilteringFeature, createFilteredRowModel, filterFns } from '@tanstack/solid-table' +import { createTable, tableFeatures, columnFilteringFeature, globalFilteringFeature, createFilteredRowModel, filterFns } from '@tanstack/solid-table' -const features = tableFeatures({ globalFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + globalFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, columns, get data() { return data() @@ -56,20 +58,20 @@ If you're not sure, you can always start with client-side filtering and paginati If you have decided that you need to implement server-side global filtering instead of using the built-in client-side global filtering, here's how you do that. -No `filteredRowModel` is needed for manual server-side global filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to `rowModels`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. +No `filteredRowModel` is needed for manual server-side global filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to `tableFeatures`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. ```tsx import { createTable, tableFeatures, + columnFilteringFeature, globalFilteringFeature, } from '@tanstack/solid-table' -const features = tableFeatures({ globalFilteringFeature }) +const features = tableFeatures({ columnFilteringFeature, globalFilteringFeature }) const table = createTable({ features, - rowModels: {}, // no filteredRowModel needed for manual server-side global filtering data, columns, manualFiltering: true, @@ -80,38 +82,38 @@ Note: When using manual global filtering, many of the options that are discussed ### Client-Side Global Filtering -If you are using the built-in client-side global filtering, add the `globalFilteringFeature` to your features and the `filteredRowModel` to your row models: +If you are using the built-in client-side global filtering, add the `globalFilteringFeature` (along with its required `columnFilteringFeature` prerequisite) and the `filteredRowModel` factory to your features: ```tsx import { createTable, tableFeatures, + columnFilteringFeature, globalFilteringFeature, createFilteredRowModel, filterFns, } from '@tanstack/solid-table' -const features = tableFeatures({ globalFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + globalFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, // other options... }) ``` ### Global Filter Function -The `globalFilterFn` option allows you to specify the filter function that will be used for global filtering. The filter function can be a string that references a built-in filter function, a string that references a custom filter function registered in the registry passed to `createFilteredRowModel`, or a custom filter function passed directly. +The `globalFilterFn` option allows you to specify the filter function that will be used for global filtering. The filter function can be a string that references a built-in filter function, a string that references a custom filter function registered in the `filterFns` slot on `tableFeatures`, or a custom filter function passed directly. ```tsx const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, data, columns, globalFilterFn: 'includesString', // built-in filter function @@ -151,7 +153,6 @@ const globalFilter = useSelector(globalFilterAtom) const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... atoms: { globalFilter: globalFilterAtom, // table.setGlobalFilter now updates globalFilterAtom @@ -166,7 +167,6 @@ const [globalFilter, setGlobalFilter] = createSignal('') const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... state: { get globalFilter() { @@ -206,7 +206,6 @@ const customFilterFn = (row, columnId, filterValue) => { const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... globalFilterFn: customFilterFn, }) @@ -219,7 +218,6 @@ If you want to set an initial global filter state when the table is initialized, ```tsx const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... initialState: { globalFilter: 'search term', // if not controlling globalFilter state, set initial state here @@ -247,7 +245,6 @@ const columns = [ //... const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, // other options... columns, enableGlobalFilter: false, // disable global filtering for all columns diff --git a/docs/framework/solid/guide/grouping.md b/docs/framework/solid/guide/grouping.md index 1d25425fc9..072e5968ca 100644 --- a/docs/framework/solid/guide/grouping.md +++ b/docs/framework/solid/guide/grouping.md @@ -15,13 +15,14 @@ Use getters for reactive inputs such as `data` when passing Solid signals to `cr ```tsx import { createTable, tableFeatures, columnGroupingFeature, createGroupedRowModel, aggregationFns } from '@tanstack/solid-table' -const features = tableFeatures({ columnGroupingFeature }) +const features = tableFeatures({ + columnGroupingFeature, + groupedRowModel: createGroupedRowModel(), + aggregationFns, +}) const table = createTable({ features, - rowModels: { - groupedRowModel: createGroupedRowModel(aggregationFns), - }, columns, get data() { return data() @@ -39,7 +40,7 @@ Grouping can also affect column order. There are 3 table features that can reord 2. Manual [Column Ordering](./column-ordering) - A manually specified column order is applied. 3. **Grouping** - If grouping is enabled, a grouping state is active, and `tableOptions.groupedColumnMode` is set to `'reorder' | 'remove'`, then the grouped columns are reordered to the start of the column flow. -To use the grouping feature, add the `columnGroupingFeature` to your features and the `groupedRowModel` to your row models. The grouped row model is responsible for grouping the rows based on the grouping state. +To use the grouping feature, add the `columnGroupingFeature` and the `groupedRowModel` factory to your features. The grouped row model is responsible for grouping the rows based on the grouping state. ```tsx import { @@ -50,13 +51,14 @@ import { aggregationFns, } from '@tanstack/solid-table' -const features = tableFeatures({ columnGroupingFeature }) +const features = tableFeatures({ + columnGroupingFeature, + groupedRowModel: createGroupedRowModel(), + aggregationFns, +}) const table = createTable({ features, - rowModels: { - groupedRowModel: createGroupedRowModel(aggregationFns), - }, // other options... }) ``` @@ -65,14 +67,16 @@ When grouping state is active, the table will add matching rows as subRows to th To allow the user to expand and collapse the grouped rows, you can use the expanding feature. ```tsx -const features = tableFeatures({ columnGroupingFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnGroupingFeature, + rowExpandingFeature, + groupedRowModel: createGroupedRowModel(), + expandedRowModel: createExpandedRowModel(), + aggregationFns, +}) const table = createTable({ features, - rowModels: { - groupedRowModel: createGroupedRowModel(aggregationFns), - expandedRowModel: createExpandedRowModel(), - }, // other options... }) ``` @@ -96,7 +100,6 @@ By default, when a column is grouped, it is moved to the start of the table. You ```tsx const table = createTable({ features, - rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) }, // other options... groupedColumnMode: 'reorder', }) @@ -129,19 +132,22 @@ There are several built-in aggregation functions that you can use: #### Custom Aggregations -You can define custom aggregation functions in the registry that you pass to `createGroupedRowModel`. The registry is a record where the keys are the names of the aggregation functions, and the values are the aggregation functions themselves. You can then reference these aggregation functions by name in a column's `aggregationFn` option. +You can define custom aggregation functions in the `aggregationFns` slot on `tableFeatures`. The slot is a record where the keys are the names of the aggregation functions, and the values are the aggregation functions themselves. You can then reference these aggregation functions by name in a column's `aggregationFn` option. ```tsx +const features = tableFeatures({ + columnGroupingFeature, + groupedRowModel: createGroupedRowModel(), + aggregationFns: { + ...aggregationFns, + myCustomAggregation: (columnId, leafRows, childRows) => { + // return the aggregated value + }, + }, +}) + const table = createTable({ features, - rowModels: { - groupedRowModel: createGroupedRowModel({ - ...aggregationFns, - myCustomAggregation: (columnId, leafRows, childRows) => { - // return the aggregated value - }, - }), - }, // other options... }) ``` @@ -154,17 +160,7 @@ const column = columnHelper.accessor('key', { }) ``` -> **TypeScript Note:** For `aggregationFn: 'myCustomAggregation'` string references to typecheck, augment the `AggregationFns` interface with a `declare module` block: -> -> ```tsx -> declare module '@tanstack/solid-table' { -> interface AggregationFns { -> myCustomAggregation: AggregationFn -> } -> } -> ``` -> -> Alternatively, skip the registry and the augmentation entirely by passing the function directly to the `aggregationFn` column option. +> **TypeScript Note:** For `aggregationFn: 'myCustomAggregation'` string references to typecheck, register the function in the `aggregationFns` slot on `tableFeatures` (as shown above). The slot is the registry; no `declare module` augmentation is needed. Alternatively, skip the registry entirely by passing the function directly to the `aggregationFn` column option. ### Manual Grouping @@ -175,7 +171,6 @@ const features = tableFeatures({ columnGroupingFeature }) const table = createTable({ features, - rowModels: {}, // no groupedRowModel needed for manual grouping // other options... manualGrouping: true, }) @@ -198,7 +193,6 @@ const grouping = useSelector(groupingAtom) const table = createTable({ features, - rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) }, // other options... atoms: { grouping: groupingAtom, // grouping APIs now update groupingAtom @@ -213,7 +207,6 @@ const [grouping, setGrouping] = createSignal([]) const table = createTable({ features, - rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) }, // other options... state: { get grouping() { diff --git a/docs/framework/solid/guide/migrating.md b/docs/framework/solid/guide/migrating.md index dcbc1c7269..1018ca8168 100644 --- a/docs/framework/solid/guide/migrating.md +++ b/docs/framework/solid/guide/migrating.md @@ -2,15 +2,19 @@ title: Migrating to TanStack Table v9 (Solid) --- +> [!NOTE] +> `v9.0.0-beta.10` introduces a breaking change in how row models are defined in order to bring increased type-safety features. Row model factories and function registries now live as slots on the `features` object instead of a separate `rowModels` option, and the factories no longer take arguments. If you migrated on an earlier beta, see the [Row Model Factories](#row-model-factories) section below for the new shape. + ## What's New in TanStack Table v9 TanStack Table v9 is a major release that makes table setup more explicit and more tree-shakeable. The Solid adapter keeps the same headless rendering model, but table creation, row model registration, state reads, and rendering helpers have changed. -### 1. Tree-shaking +### 1. Tree Shaking and Extensibility - **Features are tree-shakeable**: register only the features a table uses. -- **Row models are explicit**: client-side row processing moved from root `get*RowModel` options to the `rowModels` object. -- **Function registries moved to factories**: pass `sortFns`, `filterFns`, and `aggregationFns` to the row model factories that need them. +- **Row models are explicit**: client-side row processing moved from root `get*RowModel` options into `tableFeatures` as row model factory slots. +- **Function registries moved to features**: `sortFns`, `filterFns`, and `aggregationFns` are now slots on `tableFeatures` instead of arguments to row model factories. +- **Custom feature plugins with full type safety**: The same plugin architecture that powers the built-in features is open to your own code. Write a custom feature with its own state, options, and APIs, register it in `tableFeatures()` alongside the built-ins, and the table's types pick it all up automatically. See the [Custom Features Guide](./custom-features.md). ### 2. State Management @@ -24,6 +28,12 @@ TanStack Table v9 is a major release that makes table setup more explicit and mo - **`tableOptions()`**: compose reusable table option fragments. - **`createTableHook()`**: define app-specific table factories with shared features, row models, defaults, and components. +### 4. Improved Type Safety (No More Declaration Merging) + +- **Function registries replace `declare module` augmentation**: Custom filter, sort, and aggregation functions are registered by name in the `filterFns` / `sortFns` / `aggregationFns` slots on `tableFeatures()`. The registered keys become the valid, type-safe string values for `filterFn`, `sortFn`, `globalFilterFn`, and `aggregationFn` in your column definitions, with full inference. No more augmenting the `FilterFns` / `SortFns` / `AggregationFns` interfaces globally. +- **Per-table meta slots**: The type-only `tableMeta`, `columnMeta`, and `filterMeta` slots declare meta types for a single table instead of merging into a global interface. The `filterMeta` slot types both the `addMeta` callback in filter functions and the values read back from `row.columnFiltersMeta`. +- **Feature-gated APIs and validated prerequisites**: APIs like `table.setSorting` only exist on the table type when their feature is registered, and `tableFeatures()` validates slot prerequisites at the type level. Registering `sortFns` without `rowSortingFeature`, or `globalFilteringFeature` without `columnFilteringFeature`, is a typed error instead of a silent runtime no-op. + ### The Good News: Most Upgrades Are Opt-in - You can begin with `stockFeatures`, then audit down to explicit features. @@ -50,7 +60,7 @@ import { createTable } from '@tanstack/solid-table' const table = createTable(options) ``` -### New Required Options: `features` and `rowModels` +### New Required Option: `features` ```tsx // v8 @@ -74,7 +84,6 @@ const features = tableFeatures({}) const table = createTable({ features, - rowModels: {}, // core row model is automatic columns, get data() { return data() @@ -82,7 +91,7 @@ const table = createTable({ }) ``` -Keep `features`, `rowModels`, and column definitions outside reactive component work when they are static. +Keep `features` and column definitions outside reactive component work when they are static. --- @@ -122,7 +131,6 @@ import { createTable, stockFeatures } from '@tanstack/solid-table' const table = createTable({ features: stockFeatures, - rowModels, columns, get data() { return data() @@ -153,20 +161,20 @@ Use it as a migration shortcut, not as the preferred production end state. --- -## The `rowModels` Option +## Row Model Factories -Row models now live under `rowModels`. +Row model factories now live inside `tableFeatures` alongside feature objects. Function registries (`filterFns`, `sortFns`, `aggregationFns`) are also passed as slots in `tableFeatures` rather than as arguments to the factory functions. ### Migration Mapping -| v8 Option | v9 `rowModels` Key | v9 Factory Function | +| v8 Option | v9 `tableFeatures` Key | v9 Factory Function | |---|---|---| | `getCoreRowModel()` | (automatic) | Not needed | -| `getFilteredRowModel()` | `filteredRowModel` | `createFilteredRowModel(filterFns)` | -| `getSortedRowModel()` | `sortedRowModel` | `createSortedRowModel(sortFns)` | +| `getFilteredRowModel()` | `filteredRowModel` | `createFilteredRowModel()` | +| `getSortedRowModel()` | `sortedRowModel` | `createSortedRowModel()` | | `getPaginationRowModel()` | `paginatedRowModel` | `createPaginatedRowModel()` | | `getExpandedRowModel()` | `expandedRowModel` | `createExpandedRowModel()` | -| `getGroupedRowModel()` | `groupedRowModel` | `createGroupedRowModel(aggregationFns)` | +| `getGroupedRowModel()` | `groupedRowModel` | `createGroupedRowModel()` | | `getFacetedRowModel()` | `facetedRowModel` | `createFacetedRowModel()` | | `getFacetedMinMaxValues()` | `facetedMinMaxValues` | `createFacetedMinMaxValues()` | | `getFacetedUniqueValues()` | `facetedUniqueValues` | `createFacetedUniqueValues()` | @@ -216,17 +224,15 @@ const features = tableFeatures({ columnFilteringFeature, rowPaginationFeature, rowSortingFeature, -}) - -const rowModels = { - filteredRowModel: createFilteredRowModel(filterFns), - sortedRowModel: createSortedRowModel(sortFns), + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), paginatedRowModel: createPaginatedRowModel(), -} + filterFns, + sortFns, +}) const table = createTable({ features, - rowModels, columns, get data() { return data() @@ -308,7 +314,6 @@ const [pagination, setPagination] = createSignal({ const table = createTable({ features, - rowModels, columns, get data() { return data() @@ -347,7 +352,6 @@ function MyTable() { const table = createTable({ features, - rowModels, columns, get data() { return data() @@ -427,7 +431,6 @@ import { tableOptions } from '@tanstack/solid-table' const baseOptions = tableOptions({ features, - rowModels, defaultColumn: { minSize: 40, }, @@ -453,7 +456,6 @@ import { createTableHook } from '@tanstack/solid-table' const { createAppTable, createAppColumnHelper } = createTableHook({ features, - rowModels, }) const columnHelper = createAppColumnHelper() @@ -569,6 +571,39 @@ const features = tableFeatures({ See the new [Table and Column Meta Guide](../../../guide/table-and-column-meta) for full details on both approaches. +### `FilterFns`/`SortFns`/`AggregationFns`/`FilterMeta` Augmentation Replaced by Registry Slots + +In v8, making a custom function usable as a string reference (like `filterFn: 'fuzzy'`) required `declare module` augmentation of the `FilterFns` interface, and typing filter meta required augmenting `FilterMeta`. In v9, registering the function in the matching registry slot does both jobs with no global augmentation: + +```tsx +// v8 +declare module '@tanstack/solid-table' { + interface FilterFns { + fuzzy: FilterFn + } + interface FilterMeta { + itemRank: RankingInfo + } +} + +// v9 - register in the slot; the key becomes a valid string value +interface FuzzyFilterMeta { + itemRank?: RankingInfo +} + +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + filterMeta: metaHelper(), +}) + +// 'fuzzy' now typechecks in column defs for tables using these features +columnHelper.accessor('name', { filterFn: 'fuzzy' }) +``` + +The same pattern applies to `sortFns` (for `sortFn` string values) and `aggregationFns` (for `aggregationFn` string values). See the [Fuzzy Filtering Guide](./fuzzy-filtering.md) for a complete example. + ### `RowData` Type Restriction Prefer explicit object row types: @@ -587,9 +622,10 @@ type Person = { - [ ] Replace `createSolidTable` with `createTable`. - [ ] Define `features` using `tableFeatures()` (or use `stockFeatures`). -- [ ] Move every `get*RowModel` option into `rowModels`. +- [ ] Move every `get*RowModel` factory into `tableFeatures` as a slot (e.g. `sortedRowModel: createSortedRowModel()`). - [ ] Remove `getCoreRowModel`; the core row model is automatic. -- [ ] Pass `sortFns`, `filterFns`, and `aggregationFns` to row model factories. +- [ ] Move `sortFns`, `filterFns`, and `aggregationFns` into `tableFeatures` as slots (not as factory arguments). +- [ ] Replace `declare module` augmentation of `FilterFns`/`SortFns`/`AggregationFns` with registry-slot registration, and `FilterMeta` augmentation with the `filterMeta` slot. - [ ] Rename `sortingFn` to `sortFn`. - [ ] Add `typeof features` to column helpers and table types. - [ ] Use getters for reactive `data` and controlled `state` slices. diff --git a/docs/framework/solid/guide/pagination.md b/docs/framework/solid/guide/pagination.md index 546947b856..1eb61c39e9 100644 --- a/docs/framework/solid/guide/pagination.md +++ b/docs/framework/solid/guide/pagination.md @@ -15,13 +15,13 @@ Use getters for reactive inputs such as `data` when passing Solid signals to `cr ```tsx import { createTable, tableFeatures, rowPaginationFeature, createPaginatedRowModel } from '@tanstack/solid-table' -const features = tableFeatures({ rowPaginationFeature }) +const features = tableFeatures({ + rowPaginationFeature, + paginatedRowModel: createPaginatedRowModel(), +}) const table = createTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, get data() { return data() @@ -57,7 +57,7 @@ Alternatively, instead of paginating the data, you can render all rows of a larg #### Pagination Row Model -If you want to take advantage of the built-in client-side pagination in TanStack Table, add the `rowPaginationFeature` to your features and the `paginatedRowModel` to your row models: +If you want to take advantage of the built-in client-side pagination in TanStack Table, add the `rowPaginationFeature` and the `paginatedRowModel` factory to your features: ```tsx import { @@ -67,13 +67,13 @@ import { createPaginatedRowModel, } from '@tanstack/solid-table' -const features = tableFeatures({ rowPaginationFeature }) +const features = tableFeatures({ + rowPaginationFeature, + paginatedRowModel: createPaginatedRowModel(), +}) const table = createTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, }) @@ -100,7 +100,6 @@ const features = tableFeatures({ rowPaginationFeature }) const table = createTable({ features, - rowModels: {}, // no paginatedRowModel needed for server-side pagination columns, data, manualPagination: true, // turn off client-side pagination @@ -146,9 +145,6 @@ const pagination = useSelector(paginationAtom) const table = createTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, atoms: { @@ -167,9 +163,6 @@ const [pagination, setPagination] = createSignal({ const table = createTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, state: { @@ -186,9 +179,6 @@ Alternatively, if you have no need for managing the `pagination` state in your o ```tsx const table = createTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, initialState: { @@ -213,9 +203,6 @@ By default, `pageIndex` is reset to `0` whenever the client-side row models reco ```tsx const table = createTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, autoResetPageIndex: false, // turn off auto reset of pageIndex diff --git a/docs/framework/solid/guide/row-pinning.md b/docs/framework/solid/guide/row-pinning.md index 74fa3321cb..cbbe096c4e 100644 --- a/docs/framework/solid/guide/row-pinning.md +++ b/docs/framework/solid/guide/row-pinning.md @@ -19,7 +19,6 @@ const features = tableFeatures({ rowPinningFeature }) const table = createTable({ features, - rowModels: {}, columns, get data() { return data() @@ -38,7 +37,7 @@ There are 2 table features that can reorder rows, which happen in the following ### Enable Row Pinning -To use row pinning, add `rowPinningFeature` to your features. Row pinning does not require a row model factory, so `rowModels` can stay empty unless your table uses other row-model features. +To use row pinning, add `rowPinningFeature` to your features. Row pinning does not require a row model factory. ```tsx import { @@ -51,7 +50,6 @@ const features = tableFeatures({ rowPinningFeature }) const table = createTable({ features, - rowModels: {}, columns, data, }) @@ -73,7 +71,6 @@ You can pin rows by default with `initialState.rowPinning`: ```tsx const table = createTable({ features, - rowModels: {}, columns, data, initialState: { @@ -100,7 +97,6 @@ const rowPinning = useSelector(rowPinningAtom) // subscribe wherever it is neede const table = createTable({ features, - rowModels: {}, columns, data, atoms: { @@ -119,7 +115,6 @@ const [rowPinning, setRowPinning] = createSignal({ const table = createTable({ features, - rowModels: {}, columns, data, state: { @@ -226,7 +221,6 @@ By default, all rows can be pinned. You can disable row pinning for the whole ta ```tsx const table = createTable({ features, - rowModels: {}, columns, data, enableRowPinning: row => row.original.status !== 'archived', @@ -242,7 +236,6 @@ Set `keepPinnedRows` to `false` if pinned rows should only render when they are ```tsx const table = createTable({ features, - rowModels: {}, columns, data, keepPinnedRows: false, diff --git a/docs/framework/solid/guide/row-selection.md b/docs/framework/solid/guide/row-selection.md index 800ce5dfab..ecaaed23e4 100644 --- a/docs/framework/solid/guide/row-selection.md +++ b/docs/framework/solid/guide/row-selection.md @@ -19,7 +19,6 @@ const features = tableFeatures({ rowSelectionFeature }) const table = createTable({ features, - rowModels: {}, columns, get data() { return data() @@ -68,7 +67,6 @@ const rowSelection = useSelector(rowSelectionAtom) const table = createTable({ features, - rowModels: {}, //... atoms: { rowSelection: rowSelectionAtom, // selection APIs now update rowSelectionAtom @@ -83,7 +81,6 @@ const [rowSelection, setRowSelection] = createSignal({}) const table = createTable({ features, - rowModels: {}, //... state: { get rowSelection() { @@ -101,7 +98,6 @@ By default, the row id for each row is simply the `row.index`. If you are using ```ts const table = createTable({ features, - rowModels: {}, //... getRowId: (row) => row.uuid, // use the row's uuid from your database as the row id }) diff --git a/docs/framework/solid/guide/sorting.md b/docs/framework/solid/guide/sorting.md index f0ce1537c5..a30d834708 100644 --- a/docs/framework/solid/guide/sorting.md +++ b/docs/framework/solid/guide/sorting.md @@ -15,13 +15,14 @@ Use getters for reactive inputs such as `data` when passing Solid signals to `cr ```tsx import { createTable, tableFeatures, rowSortingFeature, createSortedRowModel, sortFns } from '@tanstack/solid-table' -const features = tableFeatures({ rowSortingFeature }) +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, +}) const table = createTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, get data() { return data() @@ -54,7 +55,6 @@ In Solid, the table's state atoms are backed by Solid signals, so `table.atoms.s ```tsx const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, //... @@ -79,7 +79,6 @@ const sorting = useSelector(sortingAtom) const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, //... @@ -96,7 +95,6 @@ const [sorting, setSorting] = createSignal([]) //... const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, //... @@ -116,7 +114,6 @@ If you do not need to control the sorting state in your own state management or ```tsx const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, //... @@ -153,7 +150,6 @@ const sorting = useSelector(sortingAtom) //... const table = createTable({ features, - rowModels: {}, // no sortedRowModel needed for manual sorting columns, data, manualSorting: true, // use pre-sorted row model instead of sorted row model @@ -169,7 +165,7 @@ Hoisting the sorting state into your own scope (with an external atom or the `st ### Client-Side Sorting -To implement client-side sorting, add the `rowSortingFeature` to your features and the `sortedRowModel` to your row models. Import `createSortedRowModel` and `sortFns` from TanStack Table: +To implement client-side sorting, add the `rowSortingFeature` and the `sortedRowModel` factory to your features. Import `createSortedRowModel` and `sortFns` from TanStack Table: ```tsx import { @@ -180,13 +176,14 @@ import { sortFns, } from '@tanstack/solid-table' -const features = tableFeatures({ rowSortingFeature }) +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, +}) const table = createTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data, }) @@ -257,35 +254,28 @@ const columns = [ } ] //... +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns: { + ...sortFns, + myCustomSortFn: (rowA, rowB, columnId) => + rowA.original[columnId] > rowB.original[columnId] + ? 1 + : rowA.original[columnId] < rowB.original[columnId] + ? -1 + : 0, + }, +}) + const table = createTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel({ - ...sortFns, - myCustomSortFn: (rowA, rowB, columnId) => - rowA.original[columnId] > rowB.original[columnId] - ? 1 - : rowA.original[columnId] < rowB.original[columnId] - ? -1 - : 0, - }), - }, columns, data, }) ``` -> **TypeScript Note:** For `sortFn: 'myCustomSortFn'` string references to typecheck, augment the `SortFns` interface with a `declare module` block: -> -> ```tsx -> declare module '@tanstack/solid-table' { -> interface SortFns { -> myCustomSortFn: SortFn -> } -> } -> ``` -> -> Alternatively, skip the registry and the augmentation entirely by passing the function directly to the `sortFn` column option. +> **TypeScript Note:** For `sortFn: 'myCustomSortFn'` string references to typecheck, register the function in the `sortFns` slot on `tableFeatures` (as shown above). The slot is the registry; no `declare module` augmentation is needed. Alternatively, skip the registry entirely by passing the function directly to the `sortFn` column option. ### Customize Sorting @@ -311,7 +301,6 @@ const columns = [ //... const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, enableSorting: false, // disable sorting for the entire table @@ -339,7 +328,6 @@ const columns = [ //... const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, sortDescFirst: true, //sort by all columns in descending order first (default is ascending for string columns and descending for number columns) @@ -406,7 +394,6 @@ Once a column is sorted and `enableSortingRemoval` is `false`, toggling the sort ```tsx const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, enableSortingRemoval: false, // disable the ability to remove sorting on columns (sorting can never return to 'none' once applied) @@ -433,7 +420,6 @@ const columns = [ //... const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, enableMultiSort: false, // disable multi-sorting for the entire table @@ -447,7 +433,6 @@ By default, the `Shift` key is used to trigger multi-sorting. You can change thi ```tsx const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, isMultiSortEvent: (e) => true, // normal click triggers multi-sorting @@ -463,7 +448,6 @@ By default, there is no limit to the number of columns that can be sorted at onc ```tsx const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, maxMultiSortColCount: 3, // only allow 3 columns to be sorted at once @@ -477,7 +461,6 @@ By default, the ability to remove multi-sorts is enabled. You can disable this b ```tsx const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, enableMultiRemove: false, // disable the ability to remove multi-sorts diff --git a/docs/framework/solid/guide/table-state.md b/docs/framework/solid/guide/table-state.md index 8701113b13..0d509750cc 100644 --- a/docs/framework/solid/guide/table-state.md +++ b/docs/framework/solid/guide/table-state.md @@ -43,14 +43,13 @@ State slices are only created for the features that are registered in `features` const features = tableFeatures({ rowPaginationFeature, rowSortingFeature, + paginatedRowModel: createPaginatedRowModel(), + sortedRowModel: createSortedRowModel(), + sortFns, }) const table = createTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - sortedRowModel: createSortedRowModel(sortFns), - }, columns, get data() { return data() @@ -99,9 +98,6 @@ Use Solid's native primitives to derive reactive values from table atoms or the ```tsx const table = createTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, get data() { return data() @@ -211,10 +207,6 @@ If you only need to customize the starting value for some table state, use `init ```tsx const table = createTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), - }, columns, get data() { return data() @@ -283,7 +275,6 @@ const dataQuery = useQuery(() => ({ const table = createTable({ features, - rowModels: {}, columns, get data() { return dataQuery.data?.rows ?? [] @@ -313,10 +304,6 @@ const [pagination, setPagination] = createSignal({ const table = createTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), - }, columns, get data() { return data() diff --git a/docs/framework/solid/guide/virtualization.md b/docs/framework/solid/guide/virtualization.md index 07fc5cf42b..d0d45cf8f0 100644 --- a/docs/framework/solid/guide/virtualization.md +++ b/docs/framework/solid/guide/virtualization.md @@ -61,13 +61,12 @@ import { createVirtualizer } from '@tanstack/solid-virtual' const features = tableFeatures({ columnSizingFeature, rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, }) const table = createTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, get data() { return data() diff --git a/docs/framework/solid/quick-start.md b/docs/framework/solid/quick-start.md index 40af6c29dc..ed917e390c 100644 --- a/docs/framework/solid/quick-start.md +++ b/docs/framework/solid/quick-start.md @@ -64,7 +64,6 @@ export function PersonTable() { const table = createTable({ key: 'person-table', // registers this table with the devtools features, - rowModels: {}, // the core row model is included by default columns, get data() { return data() @@ -114,7 +113,6 @@ export function PersonTable() { A few things to note: - `tableFeatures({})` declares which optional features the table uses. Registering only what you need keeps bundles small and gives TypeScript accurate types for the table instance. -- `rowModels: {}` is fine for a basic table because the core row model is always included. Feature row models (sorting, filtering, pagination) are registered here when you need them. - The `get data()` getter keeps the table reactive: when the signal updates, the table sees the new data. Passing `data: data()` would capture a one-time snapshot. - `FlexRender` renders the `header`, `cell`, and `footer` definitions from your columns, whether they are plain values or Solid components. It is also available on the table instance as `table.FlexRender`. - The `key` option is optional unless you use the [TanStack Table Devtools](../../devtools). The devtools identify tables by `key`, and you register a table by calling `useTanStackTableDevtools(table)` from `@tanstack/solid-table-devtools`. @@ -123,7 +121,7 @@ See the full [Basic createTable example](./examples/basic-use-table) for a runna ## Add a Feature: Sorting -Features are opt-in in v9. To make columns sortable, register `rowSortingFeature` in `tableFeatures`, register a sorted row model under `rowModels`, and wire the header click handler. +Features are opt-in in v9. To make columns sortable, register `rowSortingFeature` and the sorted row model factory in `tableFeatures`, then wire the header click handler. ```tsx import { @@ -138,6 +136,8 @@ import { For, Show, createSignal } from 'solid-js' const features = tableFeatures({ rowSortingFeature, // enables sorting APIs and state + sortedRowModel: createSortedRowModel(), // client-side sorting + sortFns, }) export function PersonTable() { @@ -146,9 +146,6 @@ export function PersonTable() { const table = createTable({ key: 'person-table', features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), // client-side sorting - }, columns, get data() { return data() @@ -193,7 +190,7 @@ export function PersonTable() { } ``` -Clicking a header now toggles between ascending, descending, and unsorted. Every other feature follows this same pattern: register the feature, register its row model if it has one, and use the APIs it adds to the table, columns, and rows. See the [Sorting Guide](./guide/sorting.md) and the [Sorting example](./examples/sorting) for custom sort functions, multi-sorting, and per-column options. +Clicking a header now toggles between ascending, descending, and unsorted. Every other feature follows this same pattern: register the feature and its row model factory (if it has one) in `tableFeatures`, then use the APIs it adds to the table, columns, and rows. See the [Sorting Guide](./guide/sorting.md) and the [Sorting example](./examples/sorting) for custom sort functions, multi-sorting, and per-column options. ## Where to Go Next @@ -204,12 +201,13 @@ Clicking a header now toggles between ascending, descending, and unsorted. Every **Composable tables.** When multiple tables in your app share features, row models, and component conventions, define them once with `createTableHook`: ```tsx -const features = tableFeatures({ rowSortingFeature }) - -const { createAppTable, createAppColumnHelper } = createTableHook({ - features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, }) + +const { createAppTable, createAppColumnHelper } = createTableHook({ features }) ``` See the [Composable Tables Guide](./guide/composable-tables.md) for the full pattern, including pre-bound cell and header components. diff --git a/docs/framework/solid/reference/functions/createTable.md b/docs/framework/solid/reference/functions/createTable.md index a11a607325..1e937b786d 100644 --- a/docs/framework/solid/reference/functions/createTable.md +++ b/docs/framework/solid/reference/functions/createTable.md @@ -9,7 +9,7 @@ title: createTable function createTable(tableOptions): SolidTable; ``` -Defined in: [createTable.ts:69](https://github.com/TanStack/table/blob/main/packages/solid-table/src/createTable.ts#L69) +Defined in: [createTable.ts:68](https://github.com/TanStack/table/blob/main/packages/solid-table/src/createTable.ts#L68) Creates a Solid table instance backed by Solid-aware TanStack Store atoms. @@ -43,7 +43,6 @@ unrelated UI. Use `table.Subscribe` to create atom-tracked render boundaries. const table = createTable( { features, - rowModels: {}, columns, data, }, diff --git a/docs/framework/solid/reference/functions/createTableHook.md b/docs/framework/solid/reference/functions/createTableHook.md index 53994c4651..00cbe65635 100644 --- a/docs/framework/solid/reference/functions/createTableHook.md +++ b/docs/framework/solid/reference/functions/createTableHook.md @@ -112,7 +112,7 @@ TFeatures is already known from the createTableHook call; TData is inferred from ##### tableOptions -`Omit`\<`TableOptions`\<`TFeatures`, `TData`\>, `"features"` \| `"rowModels"`\> +`Omit`\<`TableOptions`\<`TFeatures`, `TData`\>, `"features"`\> #### Returns @@ -250,12 +250,12 @@ export const { rowPaginationFeature, rowSortingFeature, columnFilteringFeature, - }), - rowModels: { paginatedRowModel: createPaginatedRowModel(), - sortedRowModel: createSortedRowModel(sortFns), - filteredRowModel: createFilteredRowModel(filterFns), - }, + sortedRowModel: createSortedRowModel(), + filteredRowModel: createFilteredRowModel(), + sortFns, + filterFns, + }), tableComponents: { PaginationControls, RowCount }, cellComponents: { TextCell, NumberCell }, headerComponents: { SortIndicator, ColumnFilter }, diff --git a/docs/framework/svelte/guide/column-faceting.md b/docs/framework/svelte/guide/column-faceting.md index 09787d5f32..22e70f4250 100644 --- a/docs/framework/svelte/guide/column-faceting.md +++ b/docs/framework/svelte/guide/column-faceting.md @@ -15,16 +15,18 @@ Use getters for reactive inputs such as `data` when passing Svelte state to `cre ```ts import { createTable, tableFeatures, columnFacetingFeature, columnFilteringFeature, createFacetedRowModel, createFacetedUniqueValues, createFacetedMinMaxValues, createFilteredRowModel, filterFns } from '@tanstack/svelte-table' -const features = tableFeatures({ columnFacetingFeature, columnFilteringFeature }) +const features = tableFeatures({ + columnFacetingFeature, + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + facetedRowModel: createFacetedRowModel(), + facetedUniqueValues: createFacetedUniqueValues(), + facetedMinMaxValues: createFacetedMinMaxValues(), + filterFns, +}) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - facetedRowModel: createFacetedRowModel(), - facetedUniqueValues: createFacetedUniqueValues(), - facetedMinMaxValues: createFacetedMinMaxValues(), - }, columns, get data() { return data @@ -38,7 +40,7 @@ Faceting is a feature that generates lists of values from your table's data, eit ### Column Faceting Row Models -In order to use any of the column faceting features, add the `columnFacetingFeature` to your features and the appropriate faceted row models to `rowModels`. Faceting exists to power filter UIs, so in practice you will also register the `columnFilteringFeature` and a `filteredRowModel`. Without a filtered row model, the faceted row models fall back to the pre-filtered rows and the facet values will not react to other columns' filters. +In order to use any of the column faceting features, add the `columnFacetingFeature` to your features and the appropriate faceted row model slots to `tableFeatures`. Faceting exists to power filter UIs, so in practice you will also register the `columnFilteringFeature` and a `filteredRowModel`. Without a filtered row model, the faceted row models fall back to the pre-filtered rows and the facet values will not react to other columns' filters. ```ts import { @@ -53,16 +55,18 @@ import { filterFns, } from '@tanstack/svelte-table' -const features = tableFeatures({ columnFacetingFeature, columnFilteringFeature }) +const features = tableFeatures({ + columnFacetingFeature, + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), // facet values react to other columns' filters + facetedRowModel: createFacetedRowModel(), // required for faceting (other faceted row models depend on this) + facetedMinMaxValues: createFacetedMinMaxValues(), // if you need min/max values + facetedUniqueValues: createFacetedUniqueValues(), // if you need a list of unique values + filterFns, +}) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), // facet values react to other columns' filters - facetedRowModel: createFacetedRowModel(), // required for faceting (other faceted row models depend on this) - facetedMinMaxValues: createFacetedMinMaxValues(), // if you need min/max values - facetedUniqueValues: createFacetedUniqueValues(), // if you need a list of unique values - }, columns, data, }) @@ -112,33 +116,36 @@ const [min, max] = table.getGlobalFacetedMinMaxValues() ?? [0, 1]; ### Custom (Server-Side) Faceting -Instead of using the built-in client-side faceting features, you can implement your own faceting logic on the server-side and pass the faceted values to the client-side. Supply custom `rowModels.facetedUniqueValues` and `rowModels.facetedMinMaxValues` factories. Each factory receives the table and a column ID and returns a thunk that resolves the faceted values. The column instance APIs (`column.getFacetedUniqueValues()` and `column.getFacetedMinMaxValues()`) will then return your server-provided values. +Instead of using the built-in client-side faceting features, you can implement your own faceting logic on the server-side and pass the faceted values to the client-side. Supply custom `facetedUniqueValues` and `facetedMinMaxValues` factory slots in `tableFeatures`. Each factory receives the table and a column ID and returns a thunk that resolves the faceted values. The column instance APIs (`column.getFacetedUniqueValues()` and `column.getFacetedMinMaxValues()`) will then return your server-provided values. ```ts const facetingQuery = createQuery( //... ) +const features = tableFeatures({ + columnFacetingFeature, + columnFilteringFeature, + facetedUniqueValues: (_table, columnId) => () => { + const uniqueValueMap = new Map() + //... populate from facetingQuery data for this columnId + return uniqueValueMap + }, + facetedMinMaxValues: (_table, columnId) => () => { + //... read from facetingQuery data for this columnId + return [min, max] + }, +}) + const table = createTable({ features, - rowModels: { - facetedUniqueValues: (_table, columnId) => () => { - const uniqueValueMap = new Map() - //... populate from facetingQuery data for this columnId - return uniqueValueMap - }, - facetedMinMaxValues: (_table, columnId) => () => { - //... read from facetingQuery data for this columnId - return [min, max] - }, - }, columns, data, //... }) ``` -The same factories also serve global faceting. Global faceting requests values with the internal `__global__` column ID, so you can branch on it inside the same `facetedUniqueValues` and `facetedMinMaxValues` factories to return table-wide facet values: +The same factory slots also serve global faceting. Global faceting requests values with the internal `__global__` column ID, so you can branch on it inside the same `facetedUniqueValues` and `facetedMinMaxValues` factories to return table-wide facet values: ```ts facetedUniqueValues: (_table, columnId) => () => { diff --git a/docs/framework/svelte/guide/column-filtering.md b/docs/framework/svelte/guide/column-filtering.md index 910ba936c2..d8fabec8c9 100644 --- a/docs/framework/svelte/guide/column-filtering.md +++ b/docs/framework/svelte/guide/column-filtering.md @@ -17,13 +17,14 @@ Use getters for reactive inputs such as `data` when passing Svelte state to `cre ```ts import { createTable, tableFeatures, columnFilteringFeature, createFilteredRowModel, filterFns } from '@tanstack/svelte-table' -const features = tableFeatures({ columnFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, columns, get data() { return data @@ -59,14 +60,13 @@ If you're not sure, you can always start with client-side filtering and paginati If you have decided that you need to implement server-side filtering instead of using the built-in client-side filtering, here's how you do that. -No `filteredRowModel` is needed for manual server-side filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to `rowModels`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. +No `filteredRowModel` is needed for manual server-side filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have registered a `filteredRowModel` in `tableFeatures`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. ```ts const features = tableFeatures({ columnFilteringFeature }) const table = createTable({ features, - rowModels: {}, // no filteredRowModel needed for manual server-side filtering data, columns, manualFiltering: true, @@ -88,13 +88,14 @@ import { filterFns, } from '@tanstack/svelte-table' -const features = tableFeatures({ columnFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, data, columns, }) @@ -123,7 +124,6 @@ For reactive reads that should update your UI, use `table.state.columnFilters` ( ```ts const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, //... @@ -149,7 +149,6 @@ const columnFilters = useSelector(columnFiltersAtom) // read it reactively with const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, get data() { return data @@ -171,7 +170,6 @@ const [columnFilters, setColumnFilters] = const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, get data() { return data @@ -193,7 +191,6 @@ If you do not need to control the column filter state in your own state manageme ```ts const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, //... @@ -281,33 +278,26 @@ const columns = [ } ] //... +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns: { + ...filterFns, + myCustomFilterFn: (row, columnId, filterValue) => { + return // true or false based on your custom logic + }, + startsWith: startsWithFilterFn, // defined elsewhere + }, +}) + const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel({ - ...filterFns, - myCustomFilterFn: (row, columnId, filterValue) => { - return // true or false based on your custom logic - }, - startsWith: startsWithFilterFn, // defined elsewhere - }), - }, columns, data, }) ``` -> **TypeScript Note:** For `filterFn: 'myCustomFilterFn'` string references to typecheck, augment the `FilterFns` interface with a `declare module` block: -> -> ```ts -> declare module '@tanstack/svelte-table' { -> interface FilterFns { -> myCustomFilterFn: FilterFn -> } -> } -> ``` -> -> Alternatively, skip the registry and the augmentation entirely by passing the function directly to the `filterFn` column option. See the [Fuzzy Search example](../examples/filters-fuzzy) for a complete registration with module augmentation. +> **TypeScript Note:** For `filterFn: 'myCustomFilterFn'` string references to typecheck, register the function in the `filterFns` slot of `tableFeatures({...})` as shown above. TypeScript infers the available filter function names from that registry. Alternatively, skip the registry entirely by passing the function directly to the `filterFn` column option. See the [Fuzzy Search example](../examples/filters-fuzzy) for a complete registration example. ##### Customize Filter Function Behavior @@ -359,7 +349,6 @@ const columns = [ //... const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, columns, data, enableColumnFilters: false, // disable column filtering for all columns @@ -377,14 +366,16 @@ By default, filtering is done from parent rows down, so if a parent row is filte However, if you want to allow sub-rows to be filtered and searched through, regardless of whether the parent row is filtered out, you can set the `filterFromLeafRows` table option to `true`. Setting this option to `true` will cause filtering to be done from leaf rows up, which means parent rows will be included so long as one of their child or grand-child rows is also included. ```ts -const features = tableFeatures({ columnFilteringFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnFilteringFeature, + rowExpandingFeature, + filteredRowModel: createFilteredRowModel(), + expandedRowModel: createExpandedRowModel(), + filterFns, +}) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - expandedRowModel: createExpandedRowModel(), - }, columns, data, filterFromLeafRows: true, // filter and search through sub-rows @@ -398,14 +389,16 @@ By default, filtering is done for all rows in a tree, no matter if they are root Use `maxLeafRowFilterDepth: 0` if you want to preserve a parent row's sub-rows from being filtered out while the parent row is passing the filter. ```ts -const features = tableFeatures({ columnFilteringFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnFilteringFeature, + rowExpandingFeature, + filteredRowModel: createFilteredRowModel(), + expandedRowModel: createExpandedRowModel(), + filterFns, +}) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - expandedRowModel: createExpandedRowModel(), - }, columns, data, maxLeafRowFilterDepth: 0, // only filter root level parent rows out diff --git a/docs/framework/svelte/guide/column-ordering.md b/docs/framework/svelte/guide/column-ordering.md index 31e5ae61b1..363efd37e0 100644 --- a/docs/framework/svelte/guide/column-ordering.md +++ b/docs/framework/svelte/guide/column-ordering.md @@ -19,7 +19,6 @@ const features = tableFeatures({ columnOrderingFeature }) const table = createTable({ features, - rowModels: {}, columns, get data() { return data @@ -54,7 +53,6 @@ const features = tableFeatures({ columnOrderingFeature }) const table = createTable({ features, - rowModels: {}, //... initialState: { columnOrder: ['columnId1', 'columnId2', 'columnId3'], @@ -88,7 +86,6 @@ const columnOrder = useSelector(columnOrderAtom) // subscribe wherever it is nee const table = createTable({ features, - rowModels: {}, //... atoms: { columnOrder: columnOrderAtom, @@ -112,7 +109,6 @@ const [columnOrder, setColumnOrder] = createTableState([ const table = createTable({ features, - rowModels: {}, //... state: { get columnOrder() { diff --git a/docs/framework/svelte/guide/column-pinning.md b/docs/framework/svelte/guide/column-pinning.md index 044f21c3ea..08d2465981 100644 --- a/docs/framework/svelte/guide/column-pinning.md +++ b/docs/framework/svelte/guide/column-pinning.md @@ -21,7 +21,6 @@ const features = tableFeatures({ columnPinningFeature }) const table = createTable({ features, - rowModels: {}, columns, get data() { return data @@ -65,7 +64,6 @@ const columnPinning = useSelector(columnPinningAtom) // subscribe wherever it is const table = createTable({ features, - rowModels: {}, //... atoms: { columnPinning: columnPinningAtom, @@ -88,7 +86,6 @@ const [columnPinning, setColumnPinning] = const table = createTable({ features, - rowModels: {}, //... state: { get columnPinning() { @@ -106,7 +103,6 @@ A very common use case is to pin some columns by default. You can do this by eit ```ts const table = createTable({ features, - rowModels: {}, //... initialState: { columnPinning: { diff --git a/docs/framework/svelte/guide/column-resizing.md b/docs/framework/svelte/guide/column-resizing.md index 7807206640..3db561c420 100644 --- a/docs/framework/svelte/guide/column-resizing.md +++ b/docs/framework/svelte/guide/column-resizing.md @@ -20,7 +20,6 @@ const features = tableFeatures({ columnResizingFeature }) const table = createTable({ features, - rowModels: {}, columns, get data() { return data @@ -62,7 +61,6 @@ const columns = [ const table = createTable({ features, - rowModels: {}, columns, data, }) @@ -177,7 +175,6 @@ const columnResizing = useSelector(columnResizingAtom) // subscribe wherever it const table = createTable({ features, - rowModels: {}, columns, get data() { return data @@ -206,7 +203,6 @@ const [columnResizing, setColumnResizing] = const table = createTable({ features, - rowModels: {}, columns, data, state: { diff --git a/docs/framework/svelte/guide/column-sizing.md b/docs/framework/svelte/guide/column-sizing.md index 46b011b981..6f8768bfdf 100644 --- a/docs/framework/svelte/guide/column-sizing.md +++ b/docs/framework/svelte/guide/column-sizing.md @@ -19,7 +19,6 @@ const features = tableFeatures({ columnSizingFeature }) const table = createTable({ features, - rowModels: {}, columns, get data() { return data @@ -60,7 +59,6 @@ const columns = [ const table = createTable({ features, - rowModels: {}, defaultColumn: { size: 200, // starting column size minSize: 50, // enforced during column resizing @@ -142,7 +140,6 @@ const columnSizing = useSelector(columnSizingAtom) // subscribe wherever it is n const table = createTable({ features, - rowModels: {}, columns, get data() { return data @@ -165,7 +162,6 @@ const [columnSizing, setColumnSizing] = createTableState({}) const table = createTable({ features, - rowModels: {}, columns, get data() { return data diff --git a/docs/framework/svelte/guide/column-visibility.md b/docs/framework/svelte/guide/column-visibility.md index 804ea92db6..d5b4a39abc 100644 --- a/docs/framework/svelte/guide/column-visibility.md +++ b/docs/framework/svelte/guide/column-visibility.md @@ -19,7 +19,6 @@ const features = tableFeatures({ columnVisibilityFeature }) const table = createTable({ features, - rowModels: {}, columns, get data() { return data @@ -54,7 +53,6 @@ const columnVisibility = useSelector(columnVisibilityAtom) // subscribe wherever const table = createTable({ features, - rowModels: {}, //... atoms: { columnVisibility: columnVisibilityAtom, @@ -79,7 +77,6 @@ const [columnVisibility, setColumnVisibility] = const table = createTable({ features, - rowModels: {}, //... state: { get columnVisibility() { @@ -99,7 +96,6 @@ const features = tableFeatures({ columnVisibilityFeature }) const table = createTable({ features, - rowModels: {}, //... initialState: { columnVisibility: { diff --git a/docs/framework/svelte/guide/composable-tables.md b/docs/framework/svelte/guide/composable-tables.md index 1b64f2171b..2d15ac68f5 100644 --- a/docs/framework/svelte/guide/composable-tables.md +++ b/docs/framework/svelte/guide/composable-tables.md @@ -48,6 +48,11 @@ const features = tableFeatures({ columnFilteringFeature, rowPaginationFeature, rowSortingFeature, + sortedRowModel: createSortedRowModel(), + filteredRowModel: createFilteredRowModel(), + paginatedRowModel: createPaginatedRowModel(), + sortFns, + filterFns, }) export const { @@ -58,11 +63,6 @@ export const { useHeaderContext, } = createTableHook({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - filteredRowModel: createFilteredRowModel(filterFns), - paginatedRowModel: createPaginatedRowModel(), - }, getRowId: (row) => row.id, tableComponents: { PaginationControls, diff --git a/docs/framework/svelte/guide/custom-features.md b/docs/framework/svelte/guide/custom-features.md index c80f955f2a..3d5694725c 100644 --- a/docs/framework/svelte/guide/custom-features.md +++ b/docs/framework/svelte/guide/custom-features.md @@ -264,7 +264,6 @@ const features = tableFeatures({ densityPlugin }) const table = createTable({ features, - rowModels: {}, columns, data, //.. diff --git a/docs/framework/svelte/guide/expanding.md b/docs/framework/svelte/guide/expanding.md index a629943880..3b6e74ed8b 100644 --- a/docs/framework/svelte/guide/expanding.md +++ b/docs/framework/svelte/guide/expanding.md @@ -15,13 +15,13 @@ Use getters for reactive inputs such as `data` when passing Svelte state to `cre ```ts import { createTable, tableFeatures, rowExpandingFeature, createExpandedRowModel } from '@tanstack/svelte-table' -const features = tableFeatures({ rowExpandingFeature }) +const features = tableFeatures({ + rowExpandingFeature, + expandedRowModel: createExpandedRowModel(), +}) const table = createTable({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, columns, get data() { return data @@ -52,13 +52,13 @@ import { createExpandedRowModel, } from '@tanstack/svelte-table' -const features = tableFeatures({ rowExpandingFeature }) +const features = tableFeatures({ + rowExpandingFeature, + expandedRowModel: createExpandedRowModel(), +}) const table = createTable({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, // other options... }) ``` @@ -103,9 +103,6 @@ Then you can use the getSubRows function to return the children array in each ro ```ts const table = createTable({ features, - rowModels: { - expandedRowModel: createExpandedRowModel(), - }, getSubRows: (row) => row.children, // return the children array as sub-rows // other options... }) @@ -153,7 +150,7 @@ const expanded = useSelector(expandedAtom) // read it reactively with expanded.c const table = createTable({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, + // other options... atoms: { expanded: expandedAtom, // expanding APIs now update expandedAtom @@ -170,7 +167,7 @@ const [expanded, setExpanded] = createTableState({}) const table = createTable({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, + // other options... state: { get expanded() { @@ -253,15 +250,17 @@ Use `table.setExpanded` to update the expanded state directly. `table.resetExpan By default, the filtering process starts from the parent rows and moves downwards. This means if a parent row is excluded by the filter, all its child rows will also be excluded. However, you can change this behavior by using the `filterFromLeafRows` option. When this option is enabled, the filtering process starts from the leaf (child) rows and moves upwards. This ensures that a parent row will be included in the filtered results as long as at least one of its child or grandchild rows meets the filter criteria. Additionally, you can control how deep into the child hierarchy the filter process goes by using the `maxLeafRowFilterDepth` option. This option allows you to specify the maximum depth of child rows that the filter should consider. ```ts -const features = tableFeatures({ columnFilteringFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnFilteringFeature, + rowExpandingFeature, + filteredRowModel: createFilteredRowModel(), + expandedRowModel: createExpandedRowModel(), + filterFns, +}) //... const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - expandedRowModel: createExpandedRowModel(), - }, getSubRows: (row) => row.subRows, filterFromLeafRows: true, // search through the expanded rows maxLeafRowFilterDepth: 1, // limit the depth of the expanded rows that are searched @@ -276,7 +275,7 @@ By default, expanded rows are paginated along with the rest of the table (which ```ts const table = createTable({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, + // other options... paginateExpandedRows: false, }) @@ -299,7 +298,7 @@ A common reason to set `autoResetExpanded: false` is editing data while viewing ```ts const table = createTable({ features, - rowModels: { expandedRowModel: createExpandedRowModel() }, + // other options... autoResetExpanded: false, // keep expanded state when data changes // autoResetAll: false, // or turn off all auto resets at once @@ -315,7 +314,6 @@ const features = tableFeatures({ rowExpandingFeature }) const table = createTable({ features, - rowModels: {}, // no expandedRowModel needed for manual expanding // other options... manualExpanding: true, }) diff --git a/docs/framework/svelte/guide/fuzzy-filtering.md b/docs/framework/svelte/guide/fuzzy-filtering.md index 1f621a562e..04407a4c3a 100644 --- a/docs/framework/svelte/guide/fuzzy-filtering.md +++ b/docs/framework/svelte/guide/fuzzy-filtering.md @@ -13,20 +13,21 @@ Use getters for reactive inputs such as `data` when passing Svelte state to `cre ### Svelte Setup ```ts -import { createTable, tableFeatures, columnFilteringFeature, globalFilteringFeature, rowSortingFeature, createFilteredRowModel, createSortedRowModel, filterFns, sortFns } from '@tanstack/svelte-table' +import { createTable, tableFeatures, columnFilteringFeature, globalFilteringFeature, rowSortingFeature, createFilteredRowModel, createSortedRowModel, filterFns, sortFns, metaHelper } from '@tanstack/svelte-table' const features = tableFeatures({ columnFilteringFeature, globalFilteringFeature, rowSortingFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + sortFns: { ...sortFns, fuzzy: fuzzySort }, + filterMeta: metaHelper(), }) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - sortedRowModel: createSortedRowModel(sortFns), - }, columns, get data() { return data @@ -54,9 +55,18 @@ Here's an example of a custom fuzzy filter function: ```typescript import { rankItem } from '@tanstack/match-sorter-utils' import type { RankingInfo } from '@tanstack/match-sorter-utils' -import type { FilterFn, RowData } from '@tanstack/svelte-table' +import type { FilterFn, RowData, TableFeatures } from '@tanstack/svelte-table' + +// Define the shape of the filter meta stored by fuzzyFilter +interface FuzzyFilterMeta { + itemRank?: RankingInfo +} + +// Extend the base TableFeatures type so fuzzyFilter and fuzzySort +// can type-check the filterMeta they read +type FuzzyFeatures = TableFeatures & { filterMeta: FuzzyFilterMeta } -const fuzzyFilter: FilterFn = ( +const fuzzyFilter: FilterFn = ( row, columnId, value, @@ -75,23 +85,26 @@ const fuzzyFilter: FilterFn = ( In this function, we're using the `rankItem` function from the `@tanstack/match-sorter-utils` library to rank the item. We then store the ranking information in the filter meta of the row (the `addMeta` callback is optional, so call it with optional chaining), and return whether the item passed the ranking criteria. -To reference this filter function by the string name `'fuzzy'` (and to type the stored filter meta), augment the `FilterFns` and `FilterMeta` interfaces with a `declare module` block: +To reference this filter function by the string name `'fuzzy'` and to make the stored filter meta type-safe, register the function in the `filterFns` slot and declare the meta shape in the `filterMeta` slot of `tableFeatures`: ```typescript -declare module '@tanstack/svelte-table' { - // add the fuzzy filter to the filterFns registry types - interface FilterFns { - fuzzy: FilterFn - } - interface FilterMeta { - itemRank?: RankingInfo - } -} +import { metaHelper } from '@tanstack/svelte-table' + +const features = tableFeatures({ + columnFilteringFeature, + globalFilteringFeature, + rowSortingFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + sortFns: { ...sortFns, fuzzy: fuzzySort }, + filterMeta: metaHelper(), +}) ``` ### Using Fuzzy Filtering with Global Filtering -To use fuzzy filtering with global filtering, register the fuzzy filter function in the registry passed to `createFilteredRowModel` and reference it in the `globalFilterFn` option of the table: +To use fuzzy filtering with global filtering, register the fuzzy filter function in the `filterFns` slot of `tableFeatures` and reference it in the `globalFilterFn` option of the table: ```typescript import { @@ -104,23 +117,22 @@ import { createSortedRowModel, filterFns, sortFns, + metaHelper, } from '@tanstack/svelte-table' const features = tableFeatures({ columnFilteringFeature, globalFilteringFeature, rowSortingFeature, + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), // needed if you want sorting with fuzzy rank + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + sortFns, + filterMeta: metaHelper(), }) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel({ - ...filterFns, - fuzzy: fuzzyFilter, - }), - sortedRowModel: createSortedRowModel(sortFns), // needed if you want sorting with fuzzy rank - }, columns, data, globalFilterFn: 'fuzzy', @@ -155,7 +167,7 @@ import { compareItems } from '@tanstack/match-sorter-utils' import { sortFns } from '@tanstack/svelte-table' import type { SortFn } from '@tanstack/svelte-table' -const fuzzySort: SortFn = (rowA, rowB, columnId) => { +const fuzzySort: SortFn = (rowA, rowB, columnId) => { let dir = 0 // Only sort by rank if the column has ranking information @@ -186,4 +198,4 @@ You can then pass this sorting function directly to the `sortFn` option of the c } ``` -> **Note:** Unlike `filterFn: 'fuzzy'` above, `fuzzySort` is passed as a function rather than a string. A string reference like `sortFn: 'fuzzySort'` would only work if you also registered the function in the registry passed to `createSortedRowModel` (e.g. `createSortedRowModel({ ...sortFns, fuzzySort })`) and augmented the `SortFns` interface the same way as `FilterFns`. Passing the function directly skips both steps. +> **Note:** Unlike `filterFn: 'fuzzy'` above, `fuzzySort` is passed as a function rather than a string. A string reference like `sortFn: 'fuzzySort'` would only work if you also registered the function in the `sortFns` slot of `tableFeatures` (e.g. `sortFns: { ...sortFns, fuzzySort }`). Passing the function directly skips registration. diff --git a/docs/framework/svelte/guide/global-filtering.md b/docs/framework/svelte/guide/global-filtering.md index 4ee260398e..bc2378d845 100644 --- a/docs/framework/svelte/guide/global-filtering.md +++ b/docs/framework/svelte/guide/global-filtering.md @@ -14,15 +14,17 @@ Use getters for reactive inputs such as `data` when passing Svelte state to `cre ### Svelte Setup ```ts -import { createTable, tableFeatures, globalFilteringFeature, createFilteredRowModel, filterFns } from '@tanstack/svelte-table' +import { createTable, tableFeatures, columnFilteringFeature, globalFilteringFeature, createFilteredRowModel, filterFns } from '@tanstack/svelte-table' -const features = tableFeatures({ globalFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + globalFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, columns, get data() { return data @@ -56,20 +58,20 @@ If you're not sure, you can always start with client-side filtering and paginati If you have decided that you need to implement server-side global filtering instead of using the built-in client-side global filtering, here's how you do that. -No `filteredRowModel` is needed for manual server-side global filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have added a `filteredRowModel` to `rowModels`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. +No `filteredRowModel` is needed for manual server-side global filtering. Instead, the `data` that you pass to the table should already be filtered. However, if you have registered a `filteredRowModel` in `tableFeatures`, you can tell the table to skip it by setting the `manualFiltering` option to `true`. ```ts import { createTable, tableFeatures, + columnFilteringFeature, globalFilteringFeature, } from '@tanstack/svelte-table' -const features = tableFeatures({ globalFilteringFeature }) +const features = tableFeatures({ columnFilteringFeature, globalFilteringFeature }) const table = createTable({ features, - rowModels: {}, // no filteredRowModel needed for manual server-side global filtering data, columns, manualFiltering: true, @@ -80,38 +82,38 @@ Note: When using manual global filtering, many of the options that are discussed ### Client-Side Global Filtering -If you are using the built-in client-side global filtering, add the `globalFilteringFeature` to your features and the `filteredRowModel` to your row models: +If you are using the built-in client-side global filtering, add the `globalFilteringFeature` (along with its required `columnFilteringFeature` prerequisite) to your features and the `filteredRowModel` to your row models: ```ts import { createTable, tableFeatures, + columnFilteringFeature, globalFilteringFeature, createFilteredRowModel, filterFns, } from '@tanstack/svelte-table' -const features = tableFeatures({ globalFilteringFeature }) +const features = tableFeatures({ + columnFilteringFeature, + globalFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns, +}) const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, // other options... }) ``` ### Global Filter Function -The `globalFilterFn` option allows you to specify the filter function that will be used for global filtering. The filter function can be a string that references a built-in filter function, a string that references a custom filter function registered in the registry passed to `createFilteredRowModel`, or a custom filter function passed directly. +The `globalFilterFn` option allows you to specify the filter function that will be used for global filtering. The filter function can be a string that references a built-in filter function, a string that references a custom filter function registered in the `filterFns` slot of `tableFeatures`, or a custom filter function passed directly. ```ts const table = createTable({ features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - }, data, columns, globalFilterFn: 'includesString', // built-in filter function @@ -151,7 +153,7 @@ const globalFilter = useSelector(globalFilterAtom) // read it reactively with gl const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, + // other options... atoms: { globalFilter: globalFilterAtom, // table.setGlobalFilter now updates globalFilterAtom @@ -168,7 +170,7 @@ const [globalFilter, setGlobalFilter] = createTableState('') const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, + // other options... state: { get globalFilter() { @@ -205,7 +207,7 @@ const customFilterFn = (row, columnId, filterValue) => { const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, + // other options... globalFilterFn: customFilterFn, }) @@ -218,7 +220,7 @@ If you want to set an initial global filter state when the table is initialized, ```ts const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, + // other options... initialState: { globalFilter: 'search term', // if not controlling globalFilter state, set initial state here @@ -246,7 +248,7 @@ const columns = [ //... const table = createTable({ features, - rowModels: { filteredRowModel: createFilteredRowModel(filterFns) }, + // other options... columns, enableGlobalFilter: false, // disable global filtering for all columns diff --git a/docs/framework/svelte/guide/grouping.md b/docs/framework/svelte/guide/grouping.md index 956cb2aab7..6a5c0f42cf 100644 --- a/docs/framework/svelte/guide/grouping.md +++ b/docs/framework/svelte/guide/grouping.md @@ -15,13 +15,14 @@ Use getters for reactive inputs such as `data` when passing Svelte state to `cre ```ts import { createTable, tableFeatures, columnGroupingFeature, createGroupedRowModel, aggregationFns } from '@tanstack/svelte-table' -const features = tableFeatures({ columnGroupingFeature }) +const features = tableFeatures({ + columnGroupingFeature, + groupedRowModel: createGroupedRowModel(), + aggregationFns, +}) const table = createTable({ features, - rowModels: { - groupedRowModel: createGroupedRowModel(aggregationFns), - }, columns, get data() { return data @@ -50,13 +51,14 @@ import { aggregationFns, } from '@tanstack/svelte-table' -const features = tableFeatures({ columnGroupingFeature }) +const features = tableFeatures({ + columnGroupingFeature, + groupedRowModel: createGroupedRowModel(), + aggregationFns, +}) const table = createTable({ features, - rowModels: { - groupedRowModel: createGroupedRowModel(aggregationFns), - }, // other options... }) ``` @@ -65,14 +67,16 @@ When grouping state is active, the table will add matching rows as subRows to th To allow the user to expand and collapse the grouped rows, you can use the expanding feature. ```ts -const features = tableFeatures({ columnGroupingFeature, rowExpandingFeature }) +const features = tableFeatures({ + columnGroupingFeature, + rowExpandingFeature, + groupedRowModel: createGroupedRowModel(), + expandedRowModel: createExpandedRowModel(), + aggregationFns, +}) const table = createTable({ features, - rowModels: { - groupedRowModel: createGroupedRowModel(aggregationFns), - expandedRowModel: createExpandedRowModel(), - }, // other options... }) ``` @@ -96,7 +100,6 @@ By default, when a column is grouped, it is moved to the start of the table. You ```ts const table = createTable({ features, - rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) }, // other options... groupedColumnMode: 'reorder', }) @@ -132,16 +135,19 @@ There are several built-in aggregation functions that you can use: You can define custom aggregation functions in the registry that you pass to `createGroupedRowModel`. The registry is a record where the keys are the names of the aggregation functions, and the values are the aggregation functions themselves. You can then reference these aggregation functions by name in a column's `aggregationFn` option. ```ts +const features = tableFeatures({ + columnGroupingFeature, + groupedRowModel: createGroupedRowModel(), + aggregationFns: { + ...aggregationFns, + myCustomAggregation: (columnId, leafRows, childRows) => { + // return the aggregated value + }, + }, +}) + const table = createTable({ features, - rowModels: { - groupedRowModel: createGroupedRowModel({ - ...aggregationFns, - myCustomAggregation: (columnId, leafRows, childRows) => { - // return the aggregated value - }, - }), - }, // other options... }) ``` @@ -154,17 +160,7 @@ const column = columnHelper.accessor('key', { }) ``` -> **TypeScript Note:** For `aggregationFn: 'myCustomAggregation'` string references to typecheck, augment the `AggregationFns` interface with a `declare module` block: -> -> ```ts -> declare module '@tanstack/svelte-table' { -> interface AggregationFns { -> myCustomAggregation: AggregationFn -> } -> } -> ``` -> -> Alternatively, skip the registry and the augmentation entirely by passing the function directly to the `aggregationFn` column option. +> **TypeScript Note:** For `aggregationFn: 'myCustomAggregation'` string references to typecheck, register the function in the `aggregationFns` slot of `tableFeatures({...})` as shown above. TypeScript infers the available aggregation function names from that registry. Alternatively, skip the registry entirely by passing the function directly to the `aggregationFn` column option. ### Manual Grouping @@ -175,7 +171,6 @@ const features = tableFeatures({ columnGroupingFeature }) const table = createTable({ features, - rowModels: {}, // no groupedRowModel needed for manual grouping // other options... manualGrouping: true, }) @@ -198,7 +193,7 @@ const grouping = useSelector(groupingAtom) // read it reactively with grouping.c const table = createTable({ features, - rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) }, + // other options... atoms: { grouping: groupingAtom, // grouping APIs now update groupingAtom @@ -216,7 +211,7 @@ const [grouping, setGrouping] = createTableState([]) const table = createTable({ features, - rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) }, + // other options... state: { get grouping() { diff --git a/docs/framework/svelte/guide/migrating.md b/docs/framework/svelte/guide/migrating.md index eea49109d1..9ffd2a62f6 100644 --- a/docs/framework/svelte/guide/migrating.md +++ b/docs/framework/svelte/guide/migrating.md @@ -2,15 +2,19 @@ title: Migrating to TanStack Table v9 (Svelte) --- +> [!NOTE] +> `v9.0.0-beta.10` introduces a breaking change in how row models are defined in order to bring increased type-safety features. Row model factories and function registries now live as slots on the `features` object instead of a separate `rowModels` option, and the factories no longer take arguments. If you migrated on an earlier beta, see the [Row Models](#row-models) section below for the new shape. + ## What's New in TanStack Table v9 TanStack Table v9 is a major release with explicit feature registration, row model registration, and a new atom-backed state model. The Svelte adapter was also rewritten around Svelte 5 runes. -### 1. Tree-shaking +### 1. Tree Shaking and Extensibility - **Features are tree-shakeable**: register only the table features you use. -- **Row models are explicit**: move root `get*RowModel` options into the `rowModels` object. -- **Function registries moved to factories**: pass `sortFns`, `filterFns`, and `aggregationFns` into row model factories instead of root table options. +- **Row models are explicit**: register row model factories as slots inside `tableFeatures({...})`. +- **Function registries moved to features**: pass `sortFns`, `filterFns`, and `aggregationFns` as their own slots inside `tableFeatures({...})` instead of as factory arguments or root table options. This enables tree-shaking of the functions themselves: if you only register a custom filter, you don't pay for built-in filters you never use. +- **Custom feature plugins with full type safety**: The same plugin architecture that powers the built-in features is open to your own code. Write a custom feature with its own state, options, and APIs, register it in `tableFeatures()` alongside the built-ins, and the table's types pick it all up automatically. See the [Custom Features Guide](./custom-features.md). ### 2. State Management @@ -24,13 +28,19 @@ TanStack Table v9 is a major release with explicit feature registration, row mod - **`tableOptions()`**: compose reusable option fragments. - **`createTableHook()`**: define shared Svelte table factories with pre-bound features, row models, defaults, and registered components. +### 4. Improved Type Safety (No More Declaration Merging) + +- **Function registries replace `declare module` augmentation**: Custom filter, sort, and aggregation functions are registered by name in the `filterFns` / `sortFns` / `aggregationFns` slots on `tableFeatures()`. The registered keys become the valid, type-safe string values for `filterFn`, `sortFn`, `globalFilterFn`, and `aggregationFn` in your column definitions, with full inference. No more augmenting the `FilterFns` / `SortFns` / `AggregationFns` interfaces globally. +- **Per-table meta slots**: The type-only `tableMeta`, `columnMeta`, and `filterMeta` slots declare meta types for a single table instead of merging into a global interface. The `filterMeta` slot types both the `addMeta` callback in filter functions and the values read back from `row.columnFiltersMeta`. +- **Feature-gated APIs and validated prerequisites**: APIs like `table.setSorting` only exist on the table type when their feature is registered, and `tableFeatures()` validates slot prerequisites at the type level. Registering `sortFns` without `rowSortingFeature`, or `globalFilteringFeature` without `columnFilteringFeature`, is a typed error instead of a silent runtime no-op. + ### The Good News: Most Table Logic Is Still Familiar - Column definitions keep the same basic `accessorKey`, `accessorFn`, `header`, `cell`, and `footer` shapes. - Feature APIs like `table.nextPage()`, `column.toggleSorting()`, and `row.toggleSelected()` remain the preferred way to change state. - Markup still renders header groups, rows, and cells from the table instance. -The main changes are the Svelte 5 requirement, the new `createTable` entrypoint, explicit `features` and `rowModels`, and the move from v8 writable-store patterns to v9 runes and atoms. +The main changes are the Svelte 5 requirement, the new `createTable` entrypoint, explicit `features` (including row models registered as feature slots), and the move from v8 writable-store patterns to v9 runes and atoms. --- @@ -63,7 +73,7 @@ import { createTable } from '@tanstack/svelte-table' const table = createTable(options) ``` -### New Required Options: `features` and `rowModels` +### New Required Option: `features` ```ts // v8 @@ -85,7 +95,6 @@ const features = tableFeatures({}) const table = createTable({ features, - rowModels: {}, // core row model is automatic columns, get data() { return data @@ -133,7 +142,6 @@ import { createTable, stockFeatures } from '@tanstack/svelte-table' const table = createTable({ features: stockFeatures, - rowModels, columns, get data() { return data @@ -164,24 +172,26 @@ Use it as a temporary migration shortcut. Explicit feature registration is the p --- -## The `rowModels` Option +## Row Models -Row models now live under `rowModels`. +Row model factories now live as slots directly inside `tableFeatures({...})`. The `rowModels` option no longer exists. ### Migration Mapping -| v8 Option | v9 `rowModels` Key | v9 Factory Function | +| v8 Option | v9 `tableFeatures` Slot | v9 Factory Function | |---|---|---| | `getCoreRowModel()` | (automatic) | Not needed | -| `getFilteredRowModel()` | `filteredRowModel` | `createFilteredRowModel(filterFns)` | -| `getSortedRowModel()` | `sortedRowModel` | `createSortedRowModel(sortFns)` | +| `getFilteredRowModel()` | `filteredRowModel` | `createFilteredRowModel()` | +| `getSortedRowModel()` | `sortedRowModel` | `createSortedRowModel()` | | `getPaginationRowModel()` | `paginatedRowModel` | `createPaginatedRowModel()` | | `getExpandedRowModel()` | `expandedRowModel` | `createExpandedRowModel()` | -| `getGroupedRowModel()` | `groupedRowModel` | `createGroupedRowModel(aggregationFns)` | +| `getGroupedRowModel()` | `groupedRowModel` | `createGroupedRowModel()` | | `getFacetedRowModel()` | `facetedRowModel` | `createFacetedRowModel()` | | `getFacetedMinMaxValues()` | `facetedMinMaxValues` | `createFacetedMinMaxValues()` | | `getFacetedUniqueValues()` | `facetedUniqueValues` | `createFacetedUniqueValues()` | +Function registries move to slots too: pass `filterFns`, `sortFns`, and `aggregationFns` directly to `tableFeatures` instead of as factory arguments. + ### Full Migration Example ```svelte @@ -230,19 +240,17 @@ Row models now live under `rowModels`. columnFilteringFeature, rowPaginationFeature, rowSortingFeature, - }) - - const rowModels = { - filteredRowModel: createFilteredRowModel(filterFns), - sortedRowModel: createSortedRowModel(sortFns), + filteredRowModel: createFilteredRowModel(), + sortedRowModel: createSortedRowModel(), paginatedRowModel: createPaginatedRowModel(), - } + filterFns, + sortFns, + }) let data = $state(makeData(1000)) const table = createTable({ features, - rowModels, columns, get data() { return data @@ -283,7 +291,6 @@ By default, `table.state` contains the full registered table state. ```ts const table = createTable({ features, - rowModels, columns, get data() { return data @@ -299,7 +306,6 @@ Pass a second-argument selector when you want `table.state` to contain only the const table = createTable( { features, - rowModels, columns, get data() { return data @@ -358,7 +364,6 @@ Use `createTableState` for Svelte-owned state slices that need to accept TanStac const table = createTable({ features, - rowModels, columns, get data() { return data @@ -402,7 +407,6 @@ Use external atoms when the app should own and share state slices outside the ta const table = createTable({ features, - rowModels, columns, get data() { return data @@ -506,7 +510,6 @@ import { tableOptions } from '@tanstack/svelte-table' const baseOptions = tableOptions({ features, - rowModels, defaultColumn: { minSize: 40, }, @@ -525,15 +528,12 @@ const table = createTable({ ## `createTableHook`: Composable Table Patterns -`createTableHook` creates shared Svelte table helpers with features, row models, and registered components already bound. +`createTableHook` creates shared Svelte table helpers with features (including row model slots) and registered components already bound. ```ts import { createTableHook } from '@tanstack/svelte-table' -export const { createAppTable, createAppColumnHelper } = createTableHook({ - features, - rowModels, -}) +export const { createAppTable, createAppColumnHelper } = createTableHook({ features }) const columnHelper = createAppColumnHelper() @@ -648,6 +648,39 @@ const features = tableFeatures({ See the new [Table and Column Meta Guide](../../../guide/table-and-column-meta) for full details on both approaches. +### `FilterFns`/`SortFns`/`AggregationFns`/`FilterMeta` Augmentation Replaced by Registry Slots + +In v8, making a custom function usable as a string reference (like `filterFn: 'fuzzy'`) required `declare module` augmentation of the `FilterFns` interface, and typing filter meta required augmenting `FilterMeta`. In v9, registering the function in the matching registry slot does both jobs with no global augmentation: + +```ts +// v8 +declare module '@tanstack/svelte-table' { + interface FilterFns { + fuzzy: FilterFn + } + interface FilterMeta { + itemRank: RankingInfo + } +} + +// v9 - register in the slot; the key becomes a valid string value +interface FuzzyFilterMeta { + itemRank?: RankingInfo +} + +const features = tableFeatures({ + columnFilteringFeature, + filteredRowModel: createFilteredRowModel(), + filterFns: { ...filterFns, fuzzy: fuzzyFilter }, + filterMeta: metaHelper(), +}) + +// 'fuzzy' now typechecks in column defs for tables using these features +columnHelper.accessor('name', { filterFn: 'fuzzy' }) +``` + +The same pattern applies to `sortFns` (for `sortFn` string values) and `aggregationFns` (for `aggregationFn` string values). Once a custom function is registered in a registry slot, prefer the string reference in column defs (`sortFn: 'fuzzy'`) over passing the function directly; svelte-check is stricter about function variance, and the string form sidesteps it. See the [Fuzzy Filtering Guide](./fuzzy-filtering.md) for a complete example. + ### `RowData` Type Restriction Prefer explicit object row types: @@ -668,9 +701,10 @@ type Person = { - [ ] Replace `createSvelteTable` with `createTable`. - [ ] Replace Svelte 3/4 writable-store table patterns with runes and getters. - [ ] Define `features` using `tableFeatures()` (or use `stockFeatures`). -- [ ] Move root `get*RowModel` options into `rowModels`. +- [ ] Move row model factories into `tableFeatures({...})` as slots (e.g. `filteredRowModel: createFilteredRowModel()`). - [ ] Remove `getCoreRowModel`; the core row model is automatic. -- [ ] Pass `sortFns`, `filterFns`, and `aggregationFns` to row model factories. +- [ ] Pass `sortFns`, `filterFns`, and `aggregationFns` as slots in `tableFeatures({...})` instead of as factory arguments (row model factories no longer take arguments). +- [ ] Replace `declare module` augmentation of `FilterFns`/`SortFns`/`AggregationFns` with registry-slot registration, and `FilterMeta` augmentation with the `filterMeta` slot. - [ ] Rename `sortingFn` to `sortFn`. - [ ] Add `typeof features` to column helpers and types. - [ ] Pass reactive `data` and controlled `state` slices through getters. diff --git a/docs/framework/svelte/guide/pagination.md b/docs/framework/svelte/guide/pagination.md index 6f1b0c9ad3..9d6613ff3f 100644 --- a/docs/framework/svelte/guide/pagination.md +++ b/docs/framework/svelte/guide/pagination.md @@ -15,13 +15,13 @@ Use getters for reactive inputs such as `data` when passing Svelte state to `cre ```ts import { createTable, tableFeatures, rowPaginationFeature, createPaginatedRowModel } from '@tanstack/svelte-table' -const features = tableFeatures({ rowPaginationFeature }) +const features = tableFeatures({ + rowPaginationFeature, + paginatedRowModel: createPaginatedRowModel(), +}) const table = createTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, get data() { return data @@ -67,13 +67,13 @@ import { createPaginatedRowModel, } from '@tanstack/svelte-table' -const features = tableFeatures({ rowPaginationFeature }) +const features = tableFeatures({ + rowPaginationFeature, + paginatedRowModel: createPaginatedRowModel(), +}) const table = createTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, data, }) @@ -100,7 +100,6 @@ const features = tableFeatures({ rowPaginationFeature }) const table = createTable({ features, - rowModels: {}, // no paginatedRowModel needed for server-side pagination columns, data, manualPagination: true, // turn off client-side pagination @@ -146,9 +145,7 @@ const pagination = useSelector(paginationAtom) // read it reactively with pagina const table = createTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, + columns, get data() { return data @@ -171,9 +168,7 @@ const [pagination, setPagination] = createTableState({ const table = createTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, + columns, get data() { return data @@ -192,9 +187,7 @@ Alternatively, if you have no need for managing the `pagination` state in your o ```ts const table = createTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, + columns, data, initialState: { @@ -219,9 +212,7 @@ By default, `pageIndex` is reset to `0` whenever the client-side row models reco ```ts const table = createTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, + columns, data, autoResetPageIndex: false, // turn off auto reset of pageIndex diff --git a/docs/framework/svelte/guide/row-pinning.md b/docs/framework/svelte/guide/row-pinning.md index 1a87a12339..1bbbb051be 100644 --- a/docs/framework/svelte/guide/row-pinning.md +++ b/docs/framework/svelte/guide/row-pinning.md @@ -19,7 +19,6 @@ const features = tableFeatures({ rowPinningFeature }) const table = createTable({ features, - rowModels: {}, columns, get data() { return data @@ -38,7 +37,7 @@ There are 2 table features that can reorder rows, which happen in the following ### Enable Row Pinning -To use row pinning, add `rowPinningFeature` to your features. Row pinning does not require a row model factory, so `rowModels` can stay empty unless your table uses other row-model features. +To use row pinning, add `rowPinningFeature` to your features. Row pinning does not require a row model factory. ```ts import { @@ -51,7 +50,6 @@ const features = tableFeatures({ rowPinningFeature }) const table = createTable({ features, - rowModels: {}, columns, data, }) @@ -73,7 +71,6 @@ You can pin rows by default with `initialState.rowPinning`: ```ts const table = createTable({ features, - rowModels: {}, columns, data, initialState: { @@ -100,7 +97,6 @@ const rowPinning = useSelector(rowPinningAtom) // subscribe wherever it is neede const table = createTable({ features, - rowModels: {}, columns, get data() { return data @@ -123,7 +119,6 @@ const [rowPinning, setRowPinning] = createTableState({ const table = createTable({ features, - rowModels: {}, columns, data, state: { @@ -216,7 +211,6 @@ By default, all rows can be pinned. You can disable row pinning for the whole ta ```ts const table = createTable({ features, - rowModels: {}, columns, data, enableRowPinning: row => row.original.status !== 'archived', @@ -232,7 +226,6 @@ Set `keepPinnedRows` to `false` if pinned rows should only render when they are ```ts const table = createTable({ features, - rowModels: {}, columns, data, keepPinnedRows: false, diff --git a/docs/framework/svelte/guide/row-selection.md b/docs/framework/svelte/guide/row-selection.md index b3a2c605b5..40088e0173 100644 --- a/docs/framework/svelte/guide/row-selection.md +++ b/docs/framework/svelte/guide/row-selection.md @@ -19,7 +19,6 @@ const features = tableFeatures({ rowSelectionFeature }) const table = createTable({ features, - rowModels: {}, columns, get data() { return data @@ -69,7 +68,6 @@ const rowSelection = useSelector(rowSelectionAtom) // read it reactively with ro const table = createTable({ features, - rowModels: {}, //... atoms: { rowSelection: rowSelectionAtom, // selection APIs now update rowSelectionAtom @@ -86,7 +84,6 @@ const [rowSelection, setRowSelection] = createTableState({}) const table = createTable({ features, - rowModels: {}, //... onRowSelectionChange: setRowSelection, state: { @@ -104,7 +101,6 @@ By default, the row id for each row is simply the `row.index`. If you are using ```ts const table = createTable({ features, - rowModels: {}, //... getRowId: (row) => row.uuid, // use the row's uuid from your database as the row id }) diff --git a/docs/framework/svelte/guide/sorting.md b/docs/framework/svelte/guide/sorting.md index 8e839a0133..2471b5068f 100644 --- a/docs/framework/svelte/guide/sorting.md +++ b/docs/framework/svelte/guide/sorting.md @@ -15,13 +15,14 @@ Use getters for reactive inputs such as `data` when passing Svelte state to `cre ```ts import { createTable, tableFeatures, rowSortingFeature, createSortedRowModel, sortFns } from '@tanstack/svelte-table' -const features = tableFeatures({ rowSortingFeature }) +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, +}) const table = createTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, get data() { return data @@ -54,7 +55,6 @@ For reactive reads that should update your UI, use `table.state.sorting` (select ```ts const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, //... @@ -80,7 +80,6 @@ const sorting = useSelector(sortingAtom) // read it reactively with sorting.curr const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, get data() { return data @@ -101,7 +100,6 @@ const [sorting, setSorting] = createTableState([]) const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, get data() { return data @@ -123,7 +121,6 @@ If you do not need to control the sorting state in your own state management or ```ts const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, columns, data, //... @@ -160,7 +157,6 @@ const sorting = useSelector(sortingAtom) const table = createTable({ features, - rowModels: {}, // no sortedRowModel needed for manual sorting columns, get data() { return data @@ -189,13 +185,14 @@ import { sortFns, } from '@tanstack/svelte-table' -const features = tableFeatures({ rowSortingFeature }) +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, +}) const table = createTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data, }) @@ -266,35 +263,28 @@ const columns = [ } ] //... +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns: { + ...sortFns, + myCustomSortFn: (rowA, rowB, columnId) => + rowA.original[columnId] > rowB.original[columnId] + ? 1 + : rowA.original[columnId] < rowB.original[columnId] + ? -1 + : 0, + }, +}) + const table = createTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel({ - ...sortFns, - myCustomSortFn: (rowA, rowB, columnId) => - rowA.original[columnId] > rowB.original[columnId] - ? 1 - : rowA.original[columnId] < rowB.original[columnId] - ? -1 - : 0, - }), - }, columns, data, }) ``` -> **TypeScript Note:** For `sortFn: 'myCustomSortFn'` string references to typecheck, augment the `SortFns` interface with a `declare module` block: -> -> ```ts -> declare module '@tanstack/svelte-table' { -> interface SortFns { -> myCustomSortFn: SortFn -> } -> } -> ``` -> -> Alternatively, skip the registry and the augmentation entirely by passing the function directly to the `sortFn` column option. +> **TypeScript Note:** For `sortFn: 'myCustomSortFn'` string references to typecheck, register the function in the `sortFns` slot of `tableFeatures({...})` as shown above. TypeScript infers the available sort function names from that registry. Alternatively, skip the registry entirely by passing the function directly to the `sortFn` column option. ### Customize Sorting @@ -320,7 +310,7 @@ const columns = [ //... const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, + columns, data, enableSorting: false, // disable sorting for the entire table @@ -348,7 +338,7 @@ const columns = [ //... const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, + columns, data, sortDescFirst: true, //sort by all columns in descending order first (default is ascending for string columns and descending for number columns) @@ -415,7 +405,7 @@ Once a column is sorted and `enableSortingRemoval` is `false`, toggling the sort ```ts const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, + columns, data, enableSortingRemoval: false, // disable the ability to remove sorting on columns (sorting can never return to 'none' once applied) @@ -442,7 +432,7 @@ const columns = [ //... const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, + columns, data, enableMultiSort: false, // disable multi-sorting for the entire table @@ -456,7 +446,7 @@ By default, the `Shift` key is used to trigger multi-sorting. You can change thi ```ts const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, + columns, data, isMultiSortEvent: (e) => true, // normal click triggers multi-sorting @@ -472,7 +462,7 @@ By default, there is no limit to the number of columns that can be sorted at onc ```ts const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, + columns, data, maxMultiSortColCount: 3, // only allow 3 columns to be sorted at once @@ -486,7 +476,7 @@ By default, the ability to remove multi-sorts is enabled. You can disable this b ```ts const table = createTable({ features, - rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, + columns, data, enableMultiRemove: false, // disable the ability to remove multi-sorts diff --git a/docs/framework/svelte/guide/table-state.md b/docs/framework/svelte/guide/table-state.md index 7557366518..740b15033c 100644 --- a/docs/framework/svelte/guide/table-state.md +++ b/docs/framework/svelte/guide/table-state.md @@ -44,14 +44,13 @@ State slices are only created for the features that are registered in `features` const features = tableFeatures({ rowPaginationFeature, rowSortingFeature, + paginatedRowModel: createPaginatedRowModel(), + sortedRowModel: createSortedRowModel(), + sortFns, }) const table = createTable({ features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - sortedRowModel: createSortedRowModel(sortFns), - }, columns, get data() { return data @@ -101,9 +100,6 @@ The second argument to `createTable` is a TanStack Store selector. The selected const table = createTable( { features, - rowModels: { - paginatedRowModel: createPaginatedRowModel(), - }, columns, get data() { return data @@ -172,10 +168,6 @@ If you only need to customize the starting value for some table state, use `init ```ts const table = createTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), - }, columns, get data() { return data @@ -239,7 +231,6 @@ const pagination = useSelector(paginationAtom) const table = createTable({ features, - rowModels: {}, columns, get data() { return dataQuery.data?.rows ?? [] @@ -271,10 +262,6 @@ let pagination: PaginationState = $state({ const table = createTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), - }, columns, get data() { return data diff --git a/docs/framework/svelte/guide/virtualization.md b/docs/framework/svelte/guide/virtualization.md index 57c41ae623..df1db2f029 100644 --- a/docs/framework/svelte/guide/virtualization.md +++ b/docs/framework/svelte/guide/virtualization.md @@ -61,13 +61,12 @@ import { createVirtualizer } from '@tanstack/svelte-virtual' const features = tableFeatures({ columnSizingFeature, rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, }) const table = createTable({ features, - rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - }, columns, get data() { return data diff --git a/docs/framework/svelte/quick-start.md b/docs/framework/svelte/quick-start.md index b93670b79d..cf040e8603 100644 --- a/docs/framework/svelte/quick-start.md +++ b/docs/framework/svelte/quick-start.md @@ -62,7 +62,6 @@ The component below is complete. Paste it into a Svelte 5 app and you will see a // 5. Create the table instance const table = createTable({ features, - rowModels: {}, // the core row model is included by default columns, get data() { return data // a getter keeps the table in sync with the $state rune @@ -102,7 +101,7 @@ The component below is complete. Paste it into a Svelte 5 app and you will see a A few things to note: - `tableFeatures({})` declares which optional features the table uses. Registering only what you need keeps bundles small and gives TypeScript accurate types for the table instance. -- `rowModels: {}` is fine for a basic table because the core row model is always included. Feature row models (sorting, filtering, pagination) are registered here when you need them. +- The core row model is always included automatically. Feature row models (sorting, filtering, pagination) are registered as slots directly on `tableFeatures({...})` when you need them. - Passing `data` through a getter (`get data() { return data }`) lets the table track the `$state` rune, so reassigning `data` updates the table. - The `FlexRender` component renders the `header`, `cell`, and `footer` definitions from your columns. It handles plain values, Svelte components wrapped with `renderComponent`, and snippets wrapped with `renderSnippet` (see the [Basic Snippets example](./examples/basic-snippets)). @@ -110,7 +109,7 @@ See the full [Basic createTable example](./examples/basic-create-table) for a ru ## Add a Feature: Sorting -Features are opt-in in v9. To make columns sortable, register `rowSortingFeature` in `tableFeatures`, register a sorted row model under `rowModels`, and wire a click handler into the header markup. +Features are opt-in in v9. To make columns sortable, register `rowSortingFeature` and `sortedRowModel` in `tableFeatures`, and wire a click handler into the header markup. ```svelte