Use this as the complete breaking-change checklist, not merely a quick start. V9 is treated as the current API. Migrate the app to Svelte 5 before migrating Table; the v9 adapter has no Svelte 3/4 compatibility layer.
Framework prerequisite: Svelte 5 (svelte ^5.0.0).
Recommended Migration Order
Upgrade to Svelte 5 and replace v8 stores with runes/getters.
Rename createSvelteTable to createTable.
Define explicit tableFeatures, then move row models and registries into it.
Update state reads/ownership and rendering.
Apply every shared API and type rename below.
Use stockFeatures only as a temporary audit bridge; explicit features are the production target.
const features = tableFeatures({
rowSortingFeature,
sortedRowModel: createSortedRowModel(),
sortFns: { alphanumeric: sortFn_alphanumeric },
})
const table = createTable({
features,
columns,
get data() {
return data
},
})
Construction and Feature Registration
v8
v9
createSvelteTable(options)
createTable(options)
All features bundled
Required features: tableFeatures({...})
getCoreRowModel() option
Remove; the 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. An API does not exist unless its feature is registered. Put a 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.
Svelte State Migration
Reactive option inputs must remain live: use getters for rune values such as data and controlled state slices.
table.getState().sorting becomes the narrow table.atoms.sorting.get() read. Use table.store.get() when code intentionally needs the complete state.
Table atom, store, and API reads become reactive inside templates, $derived, $derived.by, and $effect; use native $derived values for projections.
Remove second-argument selectors from createTable and createAppTable (if present from an earlier v9 version), replace table.state, and remove subscribeTable / SubscribeSource imports.
SvelteTable now has two generic parameters, AppSvelteTable has five, and useTableContext no longer accepts a selected-state generic.
For Svelte-owned controlled slices, use createTableState and matching onSortingChange, onPaginationChange, and other per-slice callbacks.
For shared ownership, provide atoms created by @tanstack/svelte-store through atoms. Never provide both atoms.pagination and state.pagination.
Subscribe to table.store to observe every state change. Do not port the removed top-level onStateChange.
Treat table.baseAtoms as internal writable state; prefer feature APIs or external atoms.
Rendering and Composition
v8
v9
flexRender(...) / <svelte:component>
<FlexRender {cell} />, <FlexRender {header} />, or <FlexRender {footer} />
Component returned directly
renderComponent(Component, props)
Svelte snippet content
renderSnippet(snippet, props)
Repeated raw options
tableOptions(...) composition
Repeated table conventions
createTableHook({ features, ... }) and its pre-bound helpers
createTableHook returns a feature-bound table creator and column helper; use it for application-wide conventions, not as a required migration step.
Complete Shared Breaking-Change Map
Instance methods
Row, cell, column, header, and related object methods now live on shared prototypes and use this. Call row.getValue(...), cell.getContext(), column.getCanSort(), and header.getContext() on their instances. Do not destructure them or pass them as bare callbacks. They are not own enumerable properties, so object spread, Object.keys, and JSON serialization do not preserve them. Table methods are not affected.
This is logical region naming, not automatic DOM direction handling. Prefer CSS inset-inline-start/inset-inline-end. columnResizeDirection is unchanged.
Feature and state splits
enablePinning splits into enableColumnPinning and enableRowPinning.
Interactive resizing requires both columnSizingFeature and columnResizingFeature; fixed widths need only sizing.
All other _-prefixed internal APIs are removed, including _getPinnedRows, _getFacetedRowModel, _getFacetedMinMaxValues, and _getFacetedUniqueValues; do not seek replacements unless a public API is documented.
getIsSomeRowsSelected() and getIsSomePageRowsSelected() now mean at least one, including all. For an indeterminate checkbox, combine “some” with !getIsAllRowsSelected() or !getIsAllPageRowsSelected().
TypeScript Migration
Core types now take 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>(); wrap arrays in columnHelper.columns([...]) for inference.
With stockFeatures, use StockFeatures as the feature type.
TableMeta and ColumnMeta declaration merging still works only after adding TFeatures first. Prefer per-table tableMeta/columnMeta: metaHelper<...>() slots.
Replace global FilterFns, SortFns, AggregationFns, and FilterMeta augmentation with filterFns, sortFns, aggregationFns, and filterMeta: metaHelper<...>() slots. Registered keys become valid string references.
Prefer explicit object row types; RowData is restricted to records or arrays.
Common Migration Failures
CRITICAL: Running v9 on Svelte 3/4
Upgrade to Svelte 5 first. Writable-store-era table setup is not a supported v9 adapter contract.
HIGH: Moving the feature but not its row model
Register both the feature and its create*RowModel() slot. Leaving get*RowModel on table options silently leaves the v9 processing pipeline incomplete.
HIGH: Snapshotting a rune value
Use get data() { return data }; a one-time data snapshot does not remain reactive.
HIGH: Keeping removed Svelte selectors
Remove second arguments from createTable and createAppTable, replace selected table.state reads with table.atoms.<slice>.get() or table.store.get(), and remove subscribeTable, SubscribeSource, and selected-state generic parameters. V9 intentionally has no compatibility layer for these APIs.
HIGH: Destructuring instance methods
Keep calls bound to row/cell/column/header instances; shallow copies do not contain prototype methods.
Final Checklist
Svelte is version 5+; old writable-store patterns are removed.
createSvelteTable is replaced by createTable.
Explicit features, row models, and function registries are in tableFeatures.
getCoreRowModel and the separate rowModels shape are removed.
Reactive inputs and controlled slices use getters/runes; state reads use v9 surfaces.
Svelte creation selectors, table.state, subscribeTable, SubscribeSource, and selected-state generic parameters are removed.
onStateChange is replaced; atom/state ownership does not overlap.
Rendering uses FlexRender, renderComponent, or renderSnippet.
Prototype method calls, pinning, sizing/resizing, sorting, row, and selection semantics are audited.
Helpers, types, meta, registries, and RowData use the v9 generic/slot shapes.
Temporary stockFeatures usage has an explicit removal plan.
API Discovery
Verify the installed target in node_modules/@tanstack/svelte-table/dist/index.d.ts and its adapter sources. Verify feature slots and the exact installed v9 APIs in node_modules/@tanstack/table-core/dist/; do not reconstruct v9 APIs from v8 memory.
1---2name: migrate-v8-to-v9-73description: Complete Svelte v8-to-v9 migration reference: Svelte 5, createTable, selector-API removal, explicit features and row-model slots, atom/rune state, rendering helpers, prototype methods, type generics, sorting, sizing, selection, and logical pinning.4---56Use this as the complete breaking-change checklist, not merely a quick start. V9 is treated as the current API. Migrate the app to Svelte 5 before migrating Table; the v9 adapter has no Svelte 3/4 compatibility layer.78Framework prerequisite: Svelte 5 (`svelte ^5.0.0`).910## Recommended Migration Order11121. Upgrade to Svelte 5 and replace v8 stores with runes/getters.132. Rename `createSvelteTable` to `createTable`.143. Define explicit `tableFeatures`, then move row models and registries into it.154. Update state reads/ownership and rendering.165. Apply every shared API and type rename below.176. Use `stockFeatures` only as a temporary audit bridge; explicit features are the production target.1819```ts20const features = tableFeatures({21 rowSortingFeature,22 sortedRowModel: createSortedRowModel(),23 sortFns: { alphanumeric: sortFn_alphanumeric },24})2526const table = createTable({27 features,28 columns,29 get data() {30 return data31 },32})33```3435## Construction and Feature Registration3637| v8 | v9 |38| -------------------------------------------- | ---------------------------------------------------------- |39| `createSvelteTable(options)` | `createTable(options)` |40| All features bundled | Required `features: tableFeatures({...})` |41| `getCoreRowModel()` option | Remove; the core row model is automatic |42| `get*RowModel()` table options | `create*RowModel()` slots in `tableFeatures` |43| `sortingFns` table option | `sortFns` feature slot |44| `filterFns` / `aggregationFns` table options | Same-named feature slots |45| Top-level `onStateChange` | Per-slice callbacks, external atoms, or store subscription |4647Available feature imports are `cellSelectionFeature`, `columnFilteringFeature`, `globalFilteringFeature`, `rowSortingFeature`, `rowPaginationFeature`, `rowSelectionFeature`, `rowExpandingFeature`, `rowPinningFeature`, `columnPinningFeature`, `columnVisibilityFeature`, `columnOrderingFeature`, `columnSizingFeature`, `columnResizingFeature`, `rowAggregationFeature`, `columnGroupingFeature`, and `columnFacetingFeature`. An API does not exist unless its feature is registered. Put a 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.4849### Row-model mapping5051| v8 option | v9 slot and factory |52| -------------------------- | ------------------------------------------------------------------- |53| `getFilteredRowModel()` | `filteredRowModel: createFilteredRowModel()` after column filtering |54| `getSortedRowModel()` | `sortedRowModel: createSortedRowModel()` after row sorting |55| `getPaginationRowModel()` | `paginatedRowModel: createPaginatedRowModel()` after pagination |56| `getExpandedRowModel()` | `expandedRowModel: createExpandedRowModel()` after expanding |57| `getGroupedRowModel()` | `groupedRowModel: createGroupedRowModel()` after grouping |58| `getFacetedRowModel()` | `facetedRowModel: createFacetedRowModel()` after faceting |59| `getFacetedMinMaxValues()` | `facetedMinMaxValues: createFacetedMinMaxValues()` |60| `getFacetedUniqueValues()` | `facetedUniqueValues: createFacetedUniqueValues()` |6162Factories 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.6364## Svelte State Migration6566- Reactive option inputs must remain live: use getters for rune values such as `data` and controlled state slices.67- `table.getState().sorting` becomes the narrow `table.atoms.sorting.get()` read. Use `table.store.get()` when code intentionally needs the complete state.68- Table atom, store, and API reads become reactive inside templates, `$derived`, `$derived.by`, and `$effect`; use native `$derived` values for projections.69- Remove second-argument selectors from `createTable` and `createAppTable` (if present from an earlier v9 version), replace `table.state`, and remove `subscribeTable` / `SubscribeSource` imports.70- `SvelteTable` now has two generic parameters, `AppSvelteTable` has five, and `useTableContext` no longer accepts a selected-state generic.71- For Svelte-owned controlled slices, use `createTableState` and matching `onSortingChange`, `onPaginationChange`, and other per-slice callbacks.72- For shared ownership, provide atoms created by `@tanstack/svelte-store` through `atoms`. Never provide both `atoms.pagination` and `state.pagination`.73- Subscribe to `table.store` to observe every state change. Do not port the removed top-level `onStateChange`.74- Treat `table.baseAtoms` as internal writable state; prefer feature APIs or external atoms.7576## Rendering and Composition7778| v8 | v9 |79| ---------------------------------------- | -------------------------------------------------------------------------------- |80| `flexRender(...)` / `<svelte:component>` | `<FlexRender {cell} />`, `<FlexRender {header} />`, or `<FlexRender {footer} />` |81| Component returned directly | `renderComponent(Component, props)` |82| Svelte snippet content | `renderSnippet(snippet, props)` |83| Repeated raw options | `tableOptions(...)` composition |84| Repeated table conventions | `createTableHook({ features, ... })` and its pre-bound helpers |8586`createTableHook` returns a feature-bound table creator and column helper; use it for application-wide conventions, not as a required migration step.8788## Complete Shared Breaking-Change Map8990### Instance methods9192Row, cell, column, header, and related object methods now live on shared prototypes and use `this`. Call `row.getValue(...)`, `cell.getContext()`, `column.getCanSort()`, and `header.getContext()` on their instances. Do not destructure them or pass them as bare callbacks. They are not own enumerable properties, so object spread, `Object.keys`, and JSON serialization do not preserve them. Table methods are not affected.9394### Logical column pinning9596V9 has no `left`/`right` aliases.9798| old | new |99| -------------------------------------------------------------- | ------------------------------------------------------------- |100| `columnPinning.left` / `.right` | `.start` / `.end` |101| `column.pin('left' \| 'right')` | `column.pin('start' \| 'end')` |102| `getIsPinned() === 'left' \| 'right'` | `'start' \| 'end'` |103| `row.getLeftVisibleCells()` / `getRightVisibleCells()` | `getStartVisibleCells()` / `getEndVisibleCells()` |104| `getLeftHeaderGroups()` / `getRightHeaderGroups()` | `getStartHeaderGroups()` / `getEndHeaderGroups()` |105| `getLeftFooterGroups()` / `getRightFooterGroups()` | `getStartFooterGroups()` / `getEndFooterGroups()` |106| `getLeftFlatHeaders()` / `getRightFlatHeaders()` | `getStartFlatHeaders()` / `getEndFlatHeaders()` |107| `getLeftLeafHeaders()` / `getRightLeafHeaders()` | `getStartLeafHeaders()` / `getEndLeafHeaders()` |108| `getLeftLeafColumns()` / `getRightLeafColumns()` | `getStartLeafColumns()` / `getEndLeafColumns()` |109| `getLeftVisibleLeafColumns()` / `getRightVisibleLeafColumns()` | `getStartVisibleLeafColumns()` / `getEndVisibleLeafColumns()` |110| `getLeftTotalSize()` / `getRightTotalSize()` | `getStartTotalSize()` / `getEndTotalSize()` |111| `column.getStart('left')` | `column.getStart('start')` |112| `column.getAfter('right')` | `column.getAfter('end')` |113| `column.getIndex('left' \| 'right')` | `column.getIndex('start' \| 'end')` |114115This is logical region naming, not automatic DOM direction handling. Prefer CSS `inset-inline-start`/`inset-inline-end`. `columnResizeDirection` is unchanged.116117### Feature and state splits118119- `enablePinning` splits into `enableColumnPinning` and `enableRowPinning`.120- Interactive resizing requires both `columnSizingFeature` and `columnResizingFeature`; fixed widths need only sizing.121- `columnSizingInfo` becomes `columnResizing`.122- `setColumnSizingInfo()` becomes `setColumnResizing()`.123- `onColumnSizingInfoChange` becomes `onColumnResizingChange`.124125### Sorting, rows, and selection126127| v8 | v9 |128| ------------------------------ | ----------------------------- |129| `sortingFn` | `sortFn` |130| `sortingFns` | `sortFns` |131| `getSortingFn()` | `getSortFn()` |132| `getAutoSortingFn()` | `getAutoSortFn()` |133| `SortingFn` / `SortingFns` | `SortFn` / `SortFns` |134| `row._getAllCellsByColumnId()` | `row.getAllCellsByColumnId()` |135136All other `_`-prefixed internal APIs are removed, including `_getPinnedRows`, `_getFacetedRowModel`, `_getFacetedMinMaxValues`, and `_getFacetedUniqueValues`; do not seek replacements unless a public API is documented.137138`getIsSomeRowsSelected()` and `getIsSomePageRowsSelected()` now mean at least one, including all. For an indeterminate checkbox, combine “some” with `!getIsAllRowsSelected()` or `!getIsAllPageRowsSelected()`.139140## TypeScript Migration141142- Core types now take `TFeatures` first: `ColumnDef<typeof features, Person>`, `Column<typeof features, Person>`, `Row<typeof features, Person>`, `Table<typeof features, Person>`.143- Replace `createColumnHelper<Person>()` with `createColumnHelper<typeof features, Person>()`; wrap arrays in `columnHelper.columns([...])` for inference.144- With `stockFeatures`, use `StockFeatures` as the feature type.145- `TableMeta` and `ColumnMeta` declaration merging still works only after adding `TFeatures` first. Prefer per-table `tableMeta`/`columnMeta: metaHelper<...>()` slots.146- Replace global `FilterFns`, `SortFns`, `AggregationFns`, and `FilterMeta` augmentation with `filterFns`, `sortFns`, `aggregationFns`, and `filterMeta: metaHelper<...>()` slots. Registered keys become valid string references.147- Prefer explicit object row types; `RowData` is restricted to records or arrays.148149## Common Migration Failures150151### CRITICAL: Running v9 on Svelte 3/4152153Upgrade to Svelte 5 first. Writable-store-era table setup is not a supported v9 adapter contract.154155### HIGH: Moving the feature but not its row model156157Register both the feature and its `create*RowModel()` slot. Leaving `get*RowModel` on table options silently leaves the v9 processing pipeline incomplete.158159### HIGH: Snapshotting a rune value160161Use `get data() { return data }`; a one-time `data` snapshot does not remain reactive.162163### HIGH: Keeping removed Svelte selectors164165Remove second arguments from `createTable` and `createAppTable`, replace selected `table.state` reads with `table.atoms.<slice>.get()` or `table.store.get()`, and remove `subscribeTable`, `SubscribeSource`, and selected-state generic parameters. V9 intentionally has no compatibility layer for these APIs.166167### HIGH: Destructuring instance methods168169Keep calls bound to row/cell/column/header instances; shallow copies do not contain prototype methods.170171## Final Checklist172173- [ ] Svelte is version 5+; old writable-store patterns are removed.174- [ ] `createSvelteTable` is replaced by `createTable`.175- [ ] Explicit features, row models, and function registries are in `tableFeatures`.176- [ ] `getCoreRowModel` and the separate `rowModels` shape are removed.177- [ ] Reactive inputs and controlled slices use getters/runes; state reads use v9 surfaces.178- [ ] Svelte creation selectors, `table.state`, `subscribeTable`, `SubscribeSource`, and selected-state generic parameters are removed.179- [ ] `onStateChange` is replaced; atom/state ownership does not overlap.180- [ ] Rendering uses `FlexRender`, `renderComponent`, or `renderSnippet`.181- [ ] Prototype method calls, pinning, sizing/resizing, sorting, row, and selection semantics are audited.182- [ ] Helpers, types, meta, registries, and `RowData` use the v9 generic/slot shapes.183- [ ] Temporary `stockFeatures` usage has an explicit removal plan.184185## API Discovery186187Verify the installed target in `node_modules/@tanstack/svelte-table/dist/index.d.ts` and its adapter sources. Verify feature slots and the exact installed v9 APIs in `node_modules/@tanstack/table-core/dist/`; do not reconstruct v9 APIs from v8 memory.
Run npx skillmds@latest add tanstack/migrate-v8-to-v9-7 in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Complete Svelte v8-to-v9 migration reference: Svelte 5, createTable, selector-API removal, explicit features and row-model slots, atom/rune state, rendering helpers, prototype methods, type generics, sorting, sizing, selection, and logical pinning. It is listed under Web & Frontend on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
TanStack (@tanstack) published this skill. Their other Agent Skills are listed on their SkillMD profile.