Complete Svelte v8-to-v9 migration reference: Svelte 5, createTable, beta.59 selector removal, explicit features and row-model slots, atom/rune state, rendering helpers, prototype methods, type generics, sorting, sizing, selection, and logical pinning.
64
78%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./packages/svelte-table/skills/migrate-v8-to-v9/SKILL.mdUse 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).
createSvelteTable to createTable.tableFeatures, then move row models and registries into it.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
},
})| 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.
| 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.
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.$derived, $derived.by, and $effect; use native $derived values for projections.createTable and createAppTable, 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.createTableState and matching onSortingChange, onPaginationChange, and other per-slice callbacks.@tanstack/svelte-store through atoms. Never provide both atoms.pagination and state.pagination.table.store to observe every state change. Do not port the removed top-level onStateChange.table.baseAtoms as internal writable state; prefer feature APIs or external atoms.| 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.
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.
There are no left/right aliases in beta.38.
| 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') |
This is logical region naming, not automatic DOM direction handling. Prefer CSS inset-inline-start/inset-inline-end. columnResizeDirection is unchanged.
enablePinning splits into enableColumnPinning and enableRowPinning.columnSizingFeature and columnResizingFeature; fixed widths need only sizing.columnSizingInfo becomes columnResizing.setColumnSizingInfo() becomes setColumnResizing().onColumnSizingInfoChange becomes onColumnResizingChange.| v8 | v9 |
|---|---|
sortingFn | sortFn |
sortingFns | sortFns |
getSortingFn() | getSortFn() |
getAutoSortingFn() | getAutoSortFn() |
SortingFn / SortingFns | SortFn / SortFns |
row._getAllCellsByColumnId() | row.getAllCellsByColumnId() |
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().
TFeatures first: ColumnDef<typeof features, Person>, Column<typeof features, Person>, Row<typeof features, Person>, Table<typeof features, Person>.createColumnHelper<Person>() with createColumnHelper<typeof features, Person>(); wrap arrays in columnHelper.columns([...]) for inference.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.FilterFns, SortFns, AggregationFns, and FilterMeta augmentation with filterFns, sortFns, aggregationFns, and filterMeta: metaHelper<...>() slots. Registered keys become valid string references.RowData is restricted to records or arrays.Upgrade to Svelte 5 first. Writable-store-era table setup is not a supported v9 adapter contract.
Register both the feature and its create*RowModel() slot. Leaving get*RowModel on table options silently leaves the v9 processing pipeline incomplete.
Use get data() { return data }; a one-time data snapshot does not remain reactive.
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. Beta.59 intentionally has no compatibility layer for these APIs.
Keep calls bound to row/cell/column/header instances; shallow copies do not contain prototype methods.
createSvelteTable is replaced by createTable.tableFeatures.getCoreRowModel and the separate rowModels shape are removed.table.state, subscribeTable, SubscribeSource, and selected-state generic parameters are removed.onStateChange is replaced; atom/state ownership does not overlap.FlexRender, renderComponent, or renderSnippet.RowData use the v9 generic/slot shapes.stockFeatures usage has an explicit removal plan.Verify the installed target in node_modules/@tanstack/svelte-table/dist/index.d.ts and its adapter sources. Verify feature slots and exact beta APIs in node_modules/@tanstack/table-core/dist/; do not reconstruct v9 APIs from v8 memory.
9e523bc
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.