UNPKG

@tanstack/vue-table

Version:

Headless UI for building powerful tables & datagrids for Vue.

186 lines (141 loc) 12.9 kB
--- name: migrate-v8-to-v9 description: > Complete Vue v8-to-v9 migration reference: useTable, explicit features and row-model slots, ref/atom state, FlexRender shorthand, prototype methods, type generics, sorting, sizing, selection, and logical pinning. metadata: type: lifecycle library: '@tanstack/vue-table' framework: vue library_version: '9.0.0' requires: - '@tanstack/table-core#migrate-v8-to-v9' - getting-started - table-state sources: - 'TanStack/table:docs/framework/vue/guide/migrating.md' - 'TanStack/table:packages/vue-table/src/index.ts' - 'TanStack/table:examples/vue/basic-use-table' --- Use this as the complete breaking-change checklist. V9 is the current API; do not stop after renaming the Vue composable. Framework prerequisite: Vue 3.2 or newer (`vue >=3.2`). ## Recommended Migration Order 1. Replace `useVueTable` with `useTable` while preserving reactive inputs. 2. Define explicit features, then move row models and registries into `tableFeatures`. 3. Update state reads, controlled ownership, and rendering. 4. Apply every shared API and type rename below. 5. Use `stockFeatures` only as a temporary audit bridge; explicit features are the production target. ```ts const features = tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel(), sortFns: { alphanumeric: sortFn_alphanumeric }, }) const data = ref(makeData()) const table = useTable({ features, columns, data }) ``` ## Construction and Feature Registration | v8 | v9 | | -------------------------------------------- | ---------------------------------------------------------- | | `useVueTable(options)` | `useTable(options)` | | All features bundled | Required `features: tableFeatures({...})` | | `getCoreRowModel()` option | Remove; core row model is automatic | | `get*RowModel()` table options | `create*RowModel()` slots in `tableFeatures` | | `sortingFns` table option | `sortFns` feature slot | | `filterFns` / `aggregationFns` table options | Same-named feature slots | | Top-level `onStateChange` | Per-slice callbacks, external atoms, or store subscription | Available feature imports are `cellSelectionFeature`, `columnFilteringFeature`, `globalFilteringFeature`, `rowSortingFeature`, `rowPaginationFeature`, `rowSelectionFeature`, `rowExpandingFeature`, `rowPinningFeature`, `columnPinningFeature`, `columnVisibilityFeature`, `columnOrderingFeature`, `columnSizingFeature`, `columnResizingFeature`, `rowAggregationFeature`, `columnGroupingFeature`, and `columnFacetingFeature`. APIs are feature-gated. Put every feature before its dependent slot in the same `tableFeatures` call. Aggregation is independent from grouping: register `rowAggregationFeature` for aggregation APIs and add `columnGroupingFeature` only for grouped rows. ### Row-model mapping | v8 option | v9 slot and factory | | -------------------------- | ------------------------------------------------------------------- | | `getFilteredRowModel()` | `filteredRowModel: createFilteredRowModel()` after column filtering | | `getSortedRowModel()` | `sortedRowModel: createSortedRowModel()` after row sorting | | `getPaginationRowModel()` | `paginatedRowModel: createPaginatedRowModel()` after pagination | | `getExpandedRowModel()` | `expandedRowModel: createExpandedRowModel()` after expanding | | `getGroupedRowModel()` | `groupedRowModel: createGroupedRowModel()` after grouping | | `getFacetedRowModel()` | `facetedRowModel: createFacetedRowModel()` after faceting | | `getFacetedMinMaxValues()` | `facetedMinMaxValues: createFacetedMinMaxValues()` | | `getFacetedUniqueValues()` | `facetedUniqueValues: createFacetedUniqueValues()` | Factories take no arguments. Register `filterFns`, `sortFns`, and `aggregationFns` as sibling feature slots holding individually imported built-ins (`filterFn_includesString`, `sortFn_alphanumeric`, `aggregationFn_sum`) under their conventional keys. The full registry objects still work but bundle every built-in. ## Vue State Migration - Pass a `ref` or `computed` as `data`; the adapter unwraps and syncs it. Do not pass `data.value`, which is only a snapshot. A getter returning `data.value` is also supported. - `table.getState().sorting` becomes the narrow `table.atoms.sorting.get()`. Use `table.store.get()` only for a full snapshot/debug output. - Wrap atom reads in Vue `computed` when deriving template values. - In JSX/render functions, `table.Subscribe` provides a fine-grained boundary. Pass the callback as the explicit `children` prop because Vue JSX element children become slots. - Controlled refs need getter-backed state slices plus per-slice callbacks that resolve value-or-function `Updater`s. - The top-level `onStateChange` is removed. Use per-slice callbacks, external atoms, or `table.store.subscribe` to observe everything. - External atoms come from `@tanstack/vue-store` and are supplied through `atoms`. Never provide both `atoms.pagination` and `state.pagination`. - `table.baseAtoms` is internal writable state; prefer feature APIs or external atoms. ## Rendering and Composition | v8 | v9 target | | -------------------------------------------------------------------------------- | ---------------------------------------------------------- | | `<FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />` | `<FlexRender :cell="cell" />` | | Manual header/footer render props | `<FlexRender :header="header" />` / `:footer="footer"` | | Repeated raw options | `tableOptions(...)` composition | | Repeated table conventions | `createTableHook({ features, ... })` and pre-bound helpers | The old `render`/`props` FlexRender shape still compiles, but shorthand is the migration target. `createTableHook` is optional and intended for application-wide conventions. ## Complete Shared Breaking-Change Map ### Instance methods Row, cell, column, header, and related methods now live on shared prototypes and use `this`. Call them on their instances. Do not destructure them, pass them bare, or expect them in object spread, `Object.keys`, or JSON. Table methods are not affected. ### Logical column pinning V9 has no `left`/`right` aliases. | old | new | | -------------------------------------------------------------- | ------------------------------------------------------------- | | `columnPinning.left` / `.right` | `.start` / `.end` | | `column.pin('left' \| 'right')` | `column.pin('start' \| 'end')` | | `getIsPinned() === 'left' \| 'right'` | `'start' \| 'end'` | | `row.getLeftVisibleCells()` / `getRightVisibleCells()` | `getStartVisibleCells()` / `getEndVisibleCells()` | | `getLeftHeaderGroups()` / `getRightHeaderGroups()` | `getStartHeaderGroups()` / `getEndHeaderGroups()` | | `getLeftFooterGroups()` / `getRightFooterGroups()` | `getStartFooterGroups()` / `getEndFooterGroups()` | | `getLeftFlatHeaders()` / `getRightFlatHeaders()` | `getStartFlatHeaders()` / `getEndFlatHeaders()` | | `getLeftLeafHeaders()` / `getRightLeafHeaders()` | `getStartLeafHeaders()` / `getEndLeafHeaders()` | | `getLeftLeafColumns()` / `getRightLeafColumns()` | `getStartLeafColumns()` / `getEndLeafColumns()` | | `getLeftVisibleLeafColumns()` / `getRightVisibleLeafColumns()` | `getStartVisibleLeafColumns()` / `getEndVisibleLeafColumns()` | | `getLeftTotalSize()` / `getRightTotalSize()` | `getStartTotalSize()` / `getEndTotalSize()` | | `column.getStart('left')` | `column.getStart('start')` | | `column.getAfter('right')` | `column.getAfter('end')` | | `column.getIndex('left' \| 'right')` | `column.getIndex('start' \| 'end')` | Use CSS `inset-inline-start`/`inset-inline-end`; logical names do not automatically set DOM direction. `columnResizeDirection` is unchanged. ### Pinning, sizing, and resizing - `enablePinning` splits into `enableColumnPinning` and `enableRowPinning`. - Interactive resizing requires `columnSizingFeature` plus `columnResizingFeature`; fixed sizing needs only the former. - `columnSizingInfo` becomes `columnResizing`. - `setColumnSizingInfo()` becomes `setColumnResizing()`. - `onColumnSizingInfoChange` becomes `onColumnResizingChange`. ### Sorting, rows, and selection | v8 | v9 | | ------------------------------ | ----------------------------- | | `sortingFn` | `sortFn` | | `sortingFns` | `sortFns` | | `getSortingFn()` | `getSortFn()` | | `getAutoSortingFn()` | `getAutoSortFn()` | | `SortingFn` / `SortingFns` | `SortFn` / `SortFns` | | `row._getAllCellsByColumnId()` | `row.getAllCellsByColumnId()` | Other `_`-prefixed internals are removed, including `_getPinnedRows`, `_getFacetedRowModel`, `_getFacetedMinMaxValues`, and `_getFacetedUniqueValues`. `getIsSomeRowsSelected()` and `getIsSomePageRowsSelected()` now mean at least one, including all. Indeterminate UI must also check `!getIsAllRowsSelected()` or `!getIsAllPageRowsSelected()`. ## TypeScript Migration - Add `TFeatures` first: `ColumnDef<typeof features, Person>`, `Column<typeof features, Person>`, `Row<typeof features, Person>`, `Table<typeof features, Person>`. - Replace `createColumnHelper<Person>()` with `createColumnHelper<typeof features, Person>()`; use `columnHelper.columns([...])` for nested-array inference. - Use `StockFeatures` when `stockFeatures` is the configuration. - Existing `TableMeta`/`ColumnMeta` declaration merging must add `TFeatures` first. Prefer per-table `tableMeta`/`columnMeta: metaHelper<...>()` slots. - Replace global `FilterFns`, `SortFns`, `AggregationFns`, and `FilterMeta` augmentation with registry slots and `filterMeta: metaHelper<...>()`; registered keys become valid strings in column defs. - `RowData` is restricted to records or arrays; prefer explicit object row types. ## Common Migration Failures ### HIGH: Renaming only the composable `useTable({ getSortedRowModel: ... })` is still a v8 configuration. Move the row model and its prerequisite feature into `tableFeatures`. ### HIGH: Unwrapping refs before useTable Pass `data`, not `data.value`, or use a getter. Preserve the reactive source. ### HIGH: Passing prototype methods bare Use `row.getValue('name')`, not `const read = row.getValue`; shallow copies also lose methods. ### MEDIUM: JSX children as slots For `table.Subscribe`, use `children={(atoms) => ...}` explicitly. ## Final Checklist - [ ] `useVueTable` is replaced with `useTable`; refs/computed inputs remain reactive. - [ ] Features, row models, and registries are in `tableFeatures`; core row model is removed. - [ ] State reads use atoms/computed or the store intentionally; `onStateChange` is removed. - [ ] External atom and controlled state ownership do not overlap. - [ ] FlexRender shorthand is adopted where applicable. - [ ] Prototype methods, pinning, sizing/resizing, sorting, row, and selection changes are audited. - [ ] Helpers, types, meta, registries, and `RowData` use v9 shapes. - [ ] Temporary `stockFeatures` usage has an explicit removal plan. ## API Discovery Inspect `node_modules/@tanstack/vue-table/dist/index.d.ts` and `useTable.d.ts`; verify feature slots and the exact installed v9 APIs in `node_modules/@tanstack/table-core/dist/`. Do not reconstruct v9 from v8 memory.