@tanstack/svelte-table 9.0.0-beta.3 → 9.0.0-beta.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -39,6 +39,7 @@
39
39
  > - [Solid Table](https://tanstack.com/table/alpha/docs/framework/solid/solid-table)
40
40
  > - [Svelte Table](https://tanstack.com/table/alpha/docs/framework/svelte/svelte-table)
41
41
  > - [Vue Table](https://tanstack.com/table/alpha/docs/framework/vue/vue-table)
42
+ > - [Alpine Table](https://tanstack.com/table/alpha/docs/framework/alpine/alpine-table)
42
43
 
43
44
  A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
44
45
 
@@ -34,7 +34,6 @@ export type SvelteTable<TFeatures extends TableFeatures, TData extends RowData,
34
34
  * const table = createTable(
35
35
  * {
36
36
  * features,
37
- * rowModels: {},
38
37
  * columns,
39
38
  * data,
40
39
  * },
@@ -17,7 +17,6 @@ import { svelteReactivity } from './reactivity.svelte';
17
17
  * const table = createTable(
18
18
  * {
19
19
  * features,
20
- * rowModels: {},
21
20
  * columns,
22
21
  * data,
23
22
  * },
@@ -32,7 +31,7 @@ export function createTable(tableOptions, selector) {
32
31
  // 1. Merge reactivity into options using mergeObjects (preserves getters)
33
32
  const mergedOptions = mergeObjects(tableOptions, {
34
33
  features: {
35
- coreReativityFeature: svelteReactivity(),
34
+ coreReactivityFeature: svelteReactivity(),
36
35
  ...tableOptions.features,
37
36
  },
38
37
  });
@@ -189,6 +189,47 @@ export type AppSvelteTable<TFeatures extends TableFeatures, TData extends RowDat
189
189
  */
190
190
  FlexRender: typeof FlexRenderSvelte;
191
191
  };
192
+ export interface CreateTableHookResult<TFeatures extends TableFeatures, TTableComponents extends Record<string, ComponentType<any>>, TCellComponents extends Record<string, ComponentType<any>>, THeaderComponents extends Record<string, ComponentType<any>>> {
193
+ /** The features object that was passed to `createTableHook`. */
194
+ appFeatures: TFeatures;
195
+ /**
196
+ * A column helper pre-bound to `TFeatures` and the registered components, so
197
+ * the cell/header/footer render props expose the bound components.
198
+ */
199
+ createAppColumnHelper: <TData extends RowData>() => AppColumnHelper<TFeatures, TData, TCellComponents, THeaderComponents>;
200
+ /**
201
+ * Creates a table with the `App*` wrapper components and registered
202
+ * `tableComponents` attached. `TData` is inferred from the `data` option.
203
+ */
204
+ createAppTable: <TData extends RowData, TSelected = TableState<TFeatures>>(tableOptions: Omit<TableOptions<TFeatures, TData>, 'features'>, selector?: (state: TableState<TFeatures>) => TSelected) => AppSvelteTable<TFeatures, TData, TSelected, TTableComponents, TCellComponents, THeaderComponents>;
205
+ /**
206
+ * Reads the table provided by the nearest `<table.AppTable>`. This is the same
207
+ * extended instance `createAppTable` returns, so the `App*` components and your
208
+ * `tableComponents` are available on it.
209
+ *
210
+ * Pass `TSelected` to match the selector you gave `createAppTable`, so
211
+ * `table.state` is typed as the selected slice. It cannot be inferred
212
+ * automatically (context does not carry the provider's generics), so it
213
+ * defaults to the full table state, which is correct for the common case of
214
+ * `createAppTable` without a selector.
215
+ */
216
+ useTableContext: <TData extends RowData = RowData, TSelected = TableState<TFeatures>>() => AppSvelteTable<TFeatures, TData, TSelected, TTableComponents, TCellComponents, THeaderComponents>;
217
+ /**
218
+ * Reads the cell provided by the nearest `<table.AppCell>`, extended with your
219
+ * `cellComponents` and a context-bound `FlexRender`.
220
+ */
221
+ useCellContext: <TValue extends CellData = CellData>() => Cell<TFeatures, any, TValue> & TCellComponents & {
222
+ FlexRender: typeof FlexRenderSvelte;
223
+ };
224
+ /**
225
+ * Reads the header provided by the nearest `<table.AppHeader>` /
226
+ * `<table.AppFooter>`, extended with your `headerComponents` and a
227
+ * context-bound `FlexRender`.
228
+ */
229
+ useHeaderContext: <TValue extends CellData = CellData>() => Header<TFeatures, any, TValue> & THeaderComponents & {
230
+ FlexRender: typeof FlexRenderSvelte;
231
+ };
232
+ }
192
233
  /**
193
234
  * Creates a custom table hook with pre-bound components for composition.
194
235
  *
@@ -213,23 +254,16 @@ export type AppSvelteTable<TFeatures extends TableFeatures, TData extends RowDat
213
254
  * rowPaginationFeature,
214
255
  * rowSortingFeature,
215
256
  * columnFilteringFeature,
216
- * }),
217
- * rowModels: {
218
257
  * paginatedRowModel: createPaginatedRowModel(),
219
- * sortedRowModel: createSortedRowModel(sortFns),
220
- * filteredRowModel: createFilteredRowModel(filterFns),
221
- * },
258
+ * sortedRowModel: createSortedRowModel(),
259
+ * filteredRowModel: createFilteredRowModel(),
260
+ * sortFns,
261
+ * filterFns,
262
+ * }),
222
263
  * tableComponents: { PaginationControls, RowCount },
223
264
  * cellComponents: { TextCell, NumberCell },
224
265
  * headerComponents: { SortIndicator, ColumnFilter },
225
266
  * })
226
267
  * ```
227
268
  */
228
- export declare function createTableHook<TFeatures extends TableFeatures, const TTableComponents extends Record<string, ComponentType<any>>, const TCellComponents extends Record<string, ComponentType<any>>, const THeaderComponents extends Record<string, ComponentType<any>>>({ tableComponents, cellComponents, headerComponents, ...defaultTableOptions }: CreateTableHookOptions<TFeatures, TTableComponents, TCellComponents, THeaderComponents>): {
229
- appFeatures: TFeatures;
230
- createAppColumnHelper: <TData extends RowData>() => AppColumnHelper<TFeatures, TData, TCellComponents, THeaderComponents>;
231
- createAppTable: <TData extends RowData, TSelected = TableState<TFeatures>>(tableOptions: Omit<TableOptions<TFeatures, TData>, "features" | "rowModels">, selector?: (state: TableState<TFeatures>) => TSelected) => AppSvelteTable<TFeatures, TData, TSelected, TTableComponents, TCellComponents, THeaderComponents>;
232
- useTableContext: <TData extends RowData = RowData>() => SvelteTable<TFeatures, TData>;
233
- useCellContext: <TValue extends CellData = unknown>() => Cell<TFeatures, any, TValue>;
234
- useHeaderContext: <TValue extends CellData = unknown>() => Header<TFeatures, any, TValue>;
235
- };
269
+ export declare function createTableHook<TFeatures extends TableFeatures, const TTableComponents extends Record<string, ComponentType<any>>, const TCellComponents extends Record<string, ComponentType<any>>, const THeaderComponents extends Record<string, ComponentType<any>>>({ tableComponents, cellComponents, headerComponents, ...defaultTableOptions }: CreateTableHookOptions<TFeatures, TTableComponents, TCellComponents, THeaderComponents>): CreateTableHookResult<TFeatures, TTableComponents, TCellComponents, THeaderComponents>;
@@ -34,12 +34,12 @@ import FlexRenderSvelte from './FlexRender.svelte';
34
34
  * rowPaginationFeature,
35
35
  * rowSortingFeature,
36
36
  * columnFilteringFeature,
37
- * }),
38
- * rowModels: {
39
37
  * paginatedRowModel: createPaginatedRowModel(),
40
- * sortedRowModel: createSortedRowModel(sortFns),
41
- * filteredRowModel: createFilteredRowModel(filterFns),
42
- * },
38
+ * sortedRowModel: createSortedRowModel(),
39
+ * filteredRowModel: createFilteredRowModel(),
40
+ * sortFns,
41
+ * filterFns,
42
+ * }),
43
43
  * tableComponents: { PaginationControls, RowCount },
44
44
  * cellComponents: { TextCell, NumberCell },
45
45
  * headerComponents: { SortIndicator, ColumnFilter },
@@ -65,6 +65,9 @@ export function createTableHook({ tableComponents, cellComponents, headerCompone
65
65
  throw new Error('`useTableContext` must be used within an `AppTable` component. ' +
66
66
  'Make sure your component is wrapped with `<table.AppTable>...</table.AppTable>`.');
67
67
  }
68
+ // `<table.AppTable>` provides the extended table (the App* wrapper
69
+ // components and `tableComponents` are Object.assign-ed onto the same
70
+ // instance `createAppTable` returns), so this asserts the runtime shape.
68
71
  return table;
69
72
  }
70
73
  /**
@@ -78,6 +81,9 @@ export function createTableHook({ tableComponents, cellComponents, headerCompone
78
81
  throw new Error('`useCellContext` must be used within an `AppCell` component. ' +
79
82
  'Make sure your component is wrapped with `<table.AppCell cell={cell}>...</table.AppCell>`.');
80
83
  }
84
+ // `<table.AppCell>` Object.assign-es `cellComponents` and `FlexRender` onto
85
+ // the same cell instance it puts in context, so this asserts the runtime
86
+ // shape.
81
87
  return cell;
82
88
  }
83
89
  /**
@@ -90,6 +96,8 @@ export function createTableHook({ tableComponents, cellComponents, headerCompone
90
96
  if (!header) {
91
97
  throw new Error('`useHeaderContext` must be used within an `AppHeader` or `AppFooter` component.');
92
98
  }
99
+ // `<table.AppHeader>` / `<table.AppFooter>` Object.assign `headerComponents`
100
+ // and `FlexRender` onto the same header instance they put in context.
93
101
  return header;
94
102
  }
95
103
  /**
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export * from '@tanstack/table-core';
2
2
  export { createTable } from './createTable.svelte';
3
3
  export type { SvelteTable } from './createTable.svelte';
4
4
  export { createTableHook } from './createTableHook.svelte';
5
- export type { AppCellContext, AppColumnDefBase, AppColumnDefTemplate, AppColumnHelper, AppDisplayColumnDef, AppGroupColumnDef, AppHeaderContext, AppSvelteTable, ComponentType, CreateTableHookOptions, } from './createTableHook.svelte';
5
+ export type { AppCellContext, AppColumnDefBase, AppColumnDefTemplate, AppColumnHelper, AppDisplayColumnDef, AppGroupColumnDef, AppHeaderContext, AppSvelteTable, ComponentType, CreateTableHookOptions, CreateTableHookResult, } from './createTableHook.svelte';
6
6
  export { createTableState } from './createTableState.svelte';
7
7
  export { default as FlexRender } from './FlexRender.svelte';
8
8
  export { subscribeTable, type SubscribeSource } from './subscribe';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/svelte-table",
3
- "version": "9.0.0-beta.3",
3
+ "version": "9.0.0-beta.30",
4
4
  "description": "Headless UI for building powerful tables & datagrids for Svelte.",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -53,14 +53,14 @@
53
53
  ],
54
54
  "dependencies": {
55
55
  "@tanstack/svelte-store": "^0.12.0",
56
- "@tanstack/table-core": "^9.0.0-beta.2"
56
+ "@tanstack/table-core": "9.0.0-beta.30"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@sveltejs/package": "^2.5.8",
60
60
  "@sveltejs/vite-plugin-svelte": "^7.1.2",
61
61
  "eslint-plugin-svelte": "^3.19.0",
62
- "svelte": "^5.56.1",
63
- "svelte-check": "^4.5.0"
62
+ "svelte": "^5.56.2",
63
+ "svelte-check": "^4.6.0"
64
64
  },
65
65
  "peerDependencies": {
66
66
  "svelte": "^5.0.0"
@@ -3,9 +3,9 @@ name: svelte/client-to-server
3
3
  description: >
4
4
  Convert a client-side Svelte table to server-side (manual) modes. Toggle `manualPagination`,
5
5
  `manualSorting`, `manualFiltering`, `manualGrouping`, `manualExpanding` for whatever the server
6
- owns, drop the matching `rowModels` factories and `features` you no longer need, supply
7
- `rowCount` for the pager, then drive the request from `table.atoms.pagination` /
8
- `table.atoms.sorting` / etc. (or external atoms you own) using rune-aware getters
6
+ owns, drop the matching row-model factory slots from `tableFeatures` and any `features` you no
7
+ longer need, supply `rowCount` for the pager, then drive the request from `table.atoms.pagination`
8
+ / `table.atoms.sorting` / etc. (or external atoms you own) using rune-aware getters
9
9
  (`get data()`, `get rowCount()`) so the table re-syncs in `$effect.pre`. Svelte 5+ only.
10
10
  type: lifecycle
11
11
  library: tanstack-table
@@ -44,8 +44,7 @@ already-paged window is perfectly valid for medium datasets.
44
44
  | `manualGrouping` | Server returns already-grouped rows | Pre-shaped data |
45
45
  | `manualExpanding` | Server resolves sub-rows | Server-provided sub-row tree |
46
46
 
47
- When a stage is manual, you can drop its row-model factory. `manualPagination: true` does not
48
- need `paginatedRowModel: createPaginatedRowModel()`.
47
+ When a stage is manual, you can drop its row-model factory slot from `tableFeatures`. `manualPagination: true` does not need `paginatedRowModel: createPaginatedRowModel()` in the features object.
49
48
 
50
49
  ## Step 1 — Identify what's moving server-side
51
50
 
@@ -111,7 +110,6 @@ const filters = useSelector(filtersAtom)
111
110
  // No row-model factories for these — server owns them.
112
111
  const table = createTable({
113
112
  features,
114
- rowModels: {},
115
113
  columns,
116
114
  get data() {
117
115
  return query.data?.rows ?? []
@@ -196,11 +194,11 @@ const table = createTable({
196
194
  columnFilteringFeature,
197
195
  rowPaginationFeature,
198
196
  rowSortingFeature,
197
+ filteredRowModel: createFilteredRowModel(), // client filters the page
198
+ sortedRowModel: createSortedRowModel(), // client sorts the page
199
+ filterFns,
200
+ sortFns,
199
201
  }),
200
- rowModels: {
201
- filteredRowModel: createFilteredRowModel(filterFns), // client filters the page
202
- sortedRowModel: createSortedRowModel(sortFns), // client sorts the page
203
- },
204
202
  columns,
205
203
  get data() {
206
204
  return query.data?.rows ?? []
@@ -116,6 +116,9 @@ Each cell type is a small Svelte component that knows which row + field it edits
116
116
  const features = tableFeatures({
117
117
  rowPaginationFeature,
118
118
  columnFilteringFeature,
119
+ filteredRowModel: createFilteredRowModel(),
120
+ paginatedRowModel: createPaginatedRowModel(),
121
+ filterFns,
119
122
  })
120
123
 
121
124
  const columnHelper = createColumnHelper<typeof features, Person>()
@@ -170,10 +173,6 @@ Each cell type is a small Svelte component that knows which row + field it edits
170
173
 
171
174
  const table = createTable({
172
175
  features,
173
- rowModels: {
174
- filteredRowModel: createFilteredRowModel(filterFns),
175
- paginatedRowModel: createPaginatedRowModel(),
176
- },
177
176
  columns,
178
177
  get data() {
179
178
  // The form is the source of truth.
@@ -95,7 +95,7 @@ Same shape, calling `table.setGlobalFilter`:
95
95
 
96
96
  ## Throttle column resizing
97
97
 
98
- `columnResizingFeature` writes to `columnSizing` continuously. v9 supports `columnResizeMode:
98
+ `columnResizingFeature` (which requires `columnSizingFeature` to also be registered in `tableFeatures`) writes to `columnSizing` continuously. v9 supports `columnResizeMode:
99
99
  'onChange' | 'onEnd'` — the default is `'onChange'` (commit per-frame). For very heavy tables,
100
100
  either:
101
101
 
@@ -58,7 +58,6 @@ the affected pipeline stages, pipe the result back through reactive getters.
58
58
 
59
59
  const table = createTable({
60
60
  features,
61
- rowModels: {},
62
61
  columns,
63
62
  get data() {
64
63
  return dataQuery.data?.rows ?? []
@@ -115,7 +114,6 @@ const dataQuery = createQuery(() => ({
115
114
 
116
115
  const table = createTable({
117
116
  features,
118
- rowModels: {},
119
117
  columns,
120
118
  get data() {
121
119
  return dataQuery.data?.rows ?? []
@@ -152,7 +150,6 @@ const table = createTable({
152
150
  rowSortingFeature,
153
151
  columnFilteringFeature,
154
152
  }),
155
- rowModels: {},
156
153
  columns,
157
154
  get data() {
158
155
  return dataQuery.data?.rows ?? []
@@ -46,7 +46,7 @@ If a slice is supplied externally via `atoms`, `table.atoms.<slice>` reads from
46
46
 
47
47
  ## The Svelte bindings (what `svelteReactivity()` actually does)
48
48
 
49
- The Svelte adapter ships `svelteReactivity()` and installs it as `coreReativityFeature`. It
49
+ The Svelte adapter ships `svelteReactivity()` and installs it as `coreReactivityFeature`. It
50
50
  maps Store primitives to runes:
51
51
 
52
52
  - Readonly atoms → `$derived.by(fn)`
@@ -83,7 +83,6 @@ The second argument to `createTable` is a TanStack Store selector. The result is
83
83
  const table = createTable(
84
84
  {
85
85
  features,
86
- rowModels: { paginatedRowModel: createPaginatedRowModel() },
87
86
  columns,
88
87
  get data() {
89
88
  return data
@@ -161,9 +160,6 @@ const pagination = useSelector(paginationAtom)
161
160
 
162
161
  const table = createTable({
163
162
  features,
164
- rowModels: {
165
- /* ... */
166
- },
167
163
  columns,
168
164
  get data() {
169
165
  return data
@@ -215,9 +211,6 @@ export const rowSelectionAtom = createAtom<RowSelectionState>({})
215
211
 
216
212
  const table = createTable({
217
213
  features,
218
- rowModels: {
219
- /* ... */
220
- },
221
214
  columns,
222
215
  get data() {
223
216
  return data
@@ -59,13 +59,17 @@ pnpm add @tanstack/svelte-virtual
59
59
  import { createVirtualizer } from '@tanstack/svelte-virtual'
60
60
  import { get } from 'svelte/store'
61
61
 
62
- const features = tableFeatures({ columnSizingFeature, rowSortingFeature })
62
+ const features = tableFeatures({
63
+ columnSizingFeature,
64
+ rowSortingFeature,
65
+ sortedRowModel: createSortedRowModel(),
66
+ sortFns,
67
+ })
63
68
 
64
69
  let data = $state<Person[]>(makeData(200_000))
65
70
 
66
71
  const table = createTable({
67
72
  features,
68
- rowModels: { sortedRowModel: createSortedRowModel(sortFns) },
69
73
  columns,
70
74
  get data() {
71
75
  return data
@@ -218,7 +222,6 @@ const flatData = $derived(
218
222
 
219
223
  const table = createTable({
220
224
  features: tableFeatures({}),
221
- rowModels: {},
222
225
  columns,
223
226
  get data() {
224
227
  return flatData
@@ -2,8 +2,8 @@
2
2
  name: svelte/getting-started
3
3
  description: >
4
4
  End-to-end first-table journey for `@tanstack/svelte-table@9` on Svelte 5. Install the adapter,
5
- declare `features` with `tableFeatures()`, register `rowModels` factories with their `*Fns`
6
- parameters, build a typed column helper with both `TFeatures` and `TData` generics, instantiate
5
+ declare `features` with `tableFeatures()` (including row-model factories and `*Fns` registries as
6
+ feature slots), build a typed column helper with both `TFeatures` and `TData` generics, instantiate
7
7
  the table with `createTable(options)` using `$state` data and `get data()` reactive option getters,
8
8
  and render with `FlexRender`. Svelte 5+ only — Svelte 3/4 must use v8.
9
9
  type: lifecycle
@@ -51,18 +51,17 @@ pnpm add @tanstack/svelte-store
51
51
  You do **not** install `@tanstack/table-core` separately — the Svelte adapter re-exports
52
52
  everything you need (column helpers, feature objects, row-model factories, types).
53
53
 
54
- ## 2. Define `features` and `rowModels`
54
+ ## 2. Define `features`
55
55
 
56
56
  v9 is explicit. You opt in to every feature and every row model. The core row model is
57
57
  included by default, so the minimum viable table is:
58
58
 
59
59
  ```ts
60
60
  const features = tableFeatures({})
61
- const rowModels = {}
62
61
  ```
63
62
 
64
- For anything beyond a flat table, register the features you'll use **and** the matching
65
- row-model factories. Row-model factories take `*Fns` registries as parameters:
63
+ For anything beyond a flat table, register the features you'll use along with the matching
64
+ row-model factories and `*Fns` registries — all as slots inside `tableFeatures`:
66
65
 
67
66
  ```ts
68
67
  import {
@@ -81,13 +80,12 @@ const features = tableFeatures({
81
80
  rowPaginationFeature,
82
81
  rowSortingFeature,
83
82
  columnFilteringFeature,
84
- })
85
-
86
- const rowModels = {
87
83
  paginatedRowModel: createPaginatedRowModel(),
88
- sortedRowModel: createSortedRowModel(sortFns),
89
- filteredRowModel: createFilteredRowModel(filterFns),
90
- }
84
+ sortedRowModel: createSortedRowModel(),
85
+ filteredRowModel: createFilteredRowModel(),
86
+ sortFns,
87
+ filterFns,
88
+ })
91
89
  ```
92
90
 
93
91
  **Skipping a feature** in `features` means its state slice does not exist on `table.atoms`,
@@ -166,7 +164,6 @@ option (`columns`, `rowCount`, `state.*`).
166
164
 
167
165
  const table = createTable({
168
166
  features,
169
- rowModels: {},
170
167
  columns,
171
168
  get data() {
172
169
  return data
@@ -230,13 +227,13 @@ focus, scroll, and any per-row component state.
230
227
  tableFeatures,
231
228
  } from '@tanstack/svelte-table'
232
229
 
233
- const features = tableFeatures({ rowPaginationFeature })
230
+ const features = tableFeatures({
231
+ rowPaginationFeature,
232
+ paginatedRowModel: createPaginatedRowModel(),
233
+ })
234
234
 
235
235
  const table = createTable({
236
236
  features,
237
- rowModels: {
238
- paginatedRowModel: createPaginatedRowModel(),
239
- },
240
237
  columns,
241
238
  get data() {
242
239
  return data
@@ -273,8 +270,7 @@ const pagination = subscribeTable(table.atoms.pagination)
273
270
 
274
271
  ## 7. `createTableHook` (when you have more than one table)
275
272
 
276
- For apps with multiple tables, define the `features`, `rowModels`, and shared components
277
- once:
273
+ For apps with multiple tables, define `features` (including row-model factories) and shared components once:
278
274
 
279
275
  ```ts
280
276
  // hooks/table.ts
@@ -289,11 +285,13 @@ import {
289
285
  } from '@tanstack/svelte-table'
290
286
 
291
287
  export const { createAppTable, createAppColumnHelper } = createTableHook({
292
- features: tableFeatures({ rowPaginationFeature, rowSortingFeature }),
293
- rowModels: {
288
+ features: tableFeatures({
289
+ rowPaginationFeature,
290
+ rowSortingFeature,
294
291
  paginatedRowModel: createPaginatedRowModel(),
295
- sortedRowModel: createSortedRowModel(sortFns),
296
- },
292
+ sortedRowModel: createSortedRowModel(),
293
+ sortFns,
294
+ }),
297
295
  })
298
296
  ```
299
297
 
@@ -319,7 +317,7 @@ export const { createAppTable, createAppColumnHelper } = createTableHook({
319
317
 
320
318
  - **Svelte 3/4.** Adapter will not work. See top of file.
321
319
  - **`createSvelteTable` / `useSvelteTable` / `getCoreRowModel`** — all v8 names. v9 uses
322
- `createTable` and `rowModels: { paginatedRowModel: createPaginatedRowModel(), ... }`.
320
+ `createTable` with row-model factories registered as slots in `tableFeatures({ paginatedRowModel: createPaginatedRowModel(), ... })`.
323
321
  - **Plain `data` instead of `get data()` getter.** Table will not see data updates. Always
324
322
  pass a reactive getter for state that lives in `$state`.
325
323
  - **Missing feature in `features`.** `table.setSorting` / `column.getCanSort` won't exist.
@@ -3,10 +3,11 @@ name: svelte/migrate-v8-to-v9
3
3
  description: >
4
4
  Mechanical migration from `@tanstack/svelte-table@8` to `@tanstack/svelte-table@9`. v9 in Svelte
5
5
  is a full rewrite — Svelte 5 runes only (no Svelte 3/4), no `/legacy` adapter (unlike React),
6
- `createSvelteTable` → `createTable`, `getCoreRowModel` / `getSortedRowModel` factories → required
7
- `features` + `rowModels` registration, `flexRender` helper → `<FlexRender>` component,
8
- writable-store `state` → rune-based getters / external atoms, `onStateChange` → per-slice
9
- `on[State]Change` or `atoms`. Plan a feature-by-feature audit, not a search-and-replace.
6
+ `createSvelteTable` → `createTable`, `getCoreRowModel` / `getSortedRowModel` factories →
7
+ row-model factories registered as slots inside `tableFeatures({...})`, `flexRender` helper →
8
+ `<FlexRender>` component, writable-store `state` → rune-based getters / external atoms,
9
+ `onStateChange` → per-slice `on[State]Change` or `atoms`. Plan a feature-by-feature audit, not a
10
+ search-and-replace.
10
11
  type: lifecycle
11
12
  library: tanstack-table
12
13
  framework: svelte
@@ -46,11 +47,11 @@ There is no third option. Trying to install v9 on a Svelte 4 codebase will error
46
47
  | -------------------------------------------------- | ------------------------------------------------------------------------------------- |
47
48
  | `createSvelteTable(options)` | `createTable(options, selector?)` |
48
49
  | `getCoreRowModel()` | included by default; no factory |
49
- | `getPaginationRowModel()` | `rowModels.paginatedRowModel: createPaginatedRowModel()` |
50
- | `getSortedRowModel()` | `rowModels.sortedRowModel: createSortedRowModel(sortFns)` |
51
- | `getFilteredRowModel()` | `rowModels.filteredRowModel: createFilteredRowModel(filterFns)` |
52
- | `getExpandedRowModel()` | `rowModels.expandedRowModel: createExpandedRowModel()` |
53
- | `getGroupedRowModel()` | `rowModels.groupedRowModel: createGroupedRowModel(aggregationFns)` |
50
+ | `getPaginationRowModel()` | `tableFeatures({ paginatedRowModel: createPaginatedRowModel() })` |
51
+ | `getSortedRowModel()` | `tableFeatures({ sortedRowModel: createSortedRowModel(), sortFns })` |
52
+ | `getFilteredRowModel()` | `tableFeatures({ filteredRowModel: createFilteredRowModel(), filterFns })` |
53
+ | `getExpandedRowModel()` | `tableFeatures({ expandedRowModel: createExpandedRowModel() })` |
54
+ | `getGroupedRowModel()` | `tableFeatures({ groupedRowModel: createGroupedRowModel(), aggregationFns })` |
54
55
  | `getFacetedRowModel()` / `MinMax` / `UniqueValues` | facet APIs auto-derived when `*Facet*Feature` registered |
55
56
  | `flexRender(template, ctx)` helper | `<FlexRender header={h} />` / `<FlexRender cell={c} />` / `<FlexRender footer={h} />` |
56
57
  | `ColumnDef<TData>` | `ColumnDef<TFeatures, TData>` (extra generic) |
@@ -79,8 +80,7 @@ For each Svelte component that uses the table:
79
80
  `import { createTable, FlexRender, tableFeatures, ... }`.
80
81
  4. **Add `features`.** Create `const features = tableFeatures({ ... only the features this
81
82
  table uses ... })`.
82
- 5. **Move row-model factories.** Every `get*RowModel: get*RowModel()` becomes a `rowModels`
83
- entry with the matching `create*RowModel(*Fns)` factory.
83
+ 5. **Move row-model factories into `tableFeatures`.** Every `get*RowModel: get*RowModel()` becomes a slot inside `tableFeatures({ ..., create*RowModel() })`. Any `*Fns` registry that was passed as a factory argument is now also a named slot in the same `tableFeatures` call.
84
84
  6. **Generic columns.** Add `<typeof features, TData>` to `ColumnDef<>` and
85
85
  `createColumnHelper<>()`. TypeScript will tell you when you missed one.
86
86
  7. **`data` as a getter.** `data: data` → `get data() { return data }`. Same for any other
@@ -99,7 +99,7 @@ table uses ... })`.
99
99
  <script lang="ts">
100
100
  // v8
101
101
  import { writable } from 'svelte/store'
102
- const options = writable({ ... })
102
+ const options = writable({ data, columns })
103
103
  $: $options.state = $state
104
104
  </script>
105
105
  ```
@@ -111,7 +111,6 @@ table uses ... })`.
111
111
  ```ts
112
112
  const table = createTable({
113
113
  features,
114
- rowModels: { paginatedRowModel: createPaginatedRowModel() },
115
114
  columns,
116
115
  get data() {
117
116
  return data
@@ -131,12 +130,17 @@ const table = createTable({
131
130
 
132
131
  const table = createTable({
133
132
  features,
134
- rowModels: { ... },
135
133
  columns,
136
- get data() { return data },
134
+ get data() {
135
+ return data
136
+ },
137
137
  state: {
138
- get sorting() { return sorting },
139
- get pagination() { return pagination },
138
+ get sorting() {
139
+ return sorting
140
+ },
141
+ get pagination() {
142
+ return pagination
143
+ },
140
144
  },
141
145
  onSortingChange: (updater) => {
142
146
  sorting = updater instanceof Function ? updater(sorting) : updater
@@ -154,16 +158,20 @@ const table = createTable({
154
158
  import { createAtom, useSelector } from '@tanstack/svelte-store'
155
159
 
156
160
  const sortingAtom = createAtom<SortingState>([])
157
- const paginationAtom = createAtom<PaginationState>({ pageIndex: 0, pageSize: 10 })
161
+ const paginationAtom = createAtom<PaginationState>({
162
+ pageIndex: 0,
163
+ pageSize: 10,
164
+ })
158
165
 
159
166
  const sorting = useSelector(sortingAtom)
160
167
  const pagination = useSelector(paginationAtom)
161
168
 
162
169
  const table = createTable({
163
170
  features,
164
- rowModels: { ... },
165
171
  columns,
166
- get data() { return data },
172
+ get data() {
173
+ return data
174
+ },
167
175
  atoms: {
168
176
  sorting: sortingAtom,
169
177
  pagination: paginationAtom,
@@ -221,8 +229,8 @@ import { renderSnippet } from '@tanstack/svelte-table'
221
229
  ## After the rewrite — verify
222
230
 
223
231
  - `pnpm test:types` (or `svelte-check`) catches missing `<typeof features, TData>` generics,
224
- missing `features` / `rowModels`, and feature APIs called on tables that didn't register
225
- them.
232
+ missing `features` or row-model factory slots, and feature APIs called on tables that didn't
233
+ register them.
226
234
  - `pnpm build` should pass with the new `features` set. Bundle should shrink — only the
227
235
  features you register are included.
228
236
  - Click through every table screen. Reset buttons, multi-sort, pagination reset on filter,
@@ -241,9 +249,9 @@ import { renderSnippet } from '@tanstack/svelte-table'
241
249
  changed" bugs during migration.
242
250
  - **Plain `features: { rowPaginationFeature }` instead of `tableFeatures({...})`.** Loses
243
251
  inference; you'll get `any` everywhere.
244
- - **Forgetting feature registration after copying the row-model factory.** Adding
245
- `paginatedRowModel: createPaginatedRowModel()` without `rowPaginationFeature` in
246
- `features` does nothing.
252
+ - **Forgetting feature registration after adding the row-model factory slot.** Adding
253
+ `paginatedRowModel: createPaginatedRowModel()` to `tableFeatures` without also including
254
+ `rowPaginationFeature` does nothing.
247
255
  - **Reimplementing v8 helpers** (`flexRender`-style functions, manual store subscriptions,
248
256
  hand-rolled selectors) instead of using `FlexRender` / `subscribeTable` / atom selectors.
249
257
  That's the #1 AI tell on this migration.
@@ -69,8 +69,9 @@ If you find yourself running a table without a feature's UI ever showing, drop t
69
69
  `createTable` syncs options in `$effect.pre`. If any of these identities flip every component
70
70
  run, the table re-syncs more than it needs to.
71
71
 
72
- - **`features` and `rowModels`**: declare at module scope, not inside the component
73
- function, and never inside `$derived` / `$effect`.
72
+ - **`features`**: declare at module scope, not inside the component function, and never
73
+ inside `$derived` / `$effect`. The `features` object now also carries row-model factories
74
+ and `*Fns` registries, so keeping it stable prevents unnecessary re-sync.
74
75
  - **`columns`**: same — module scope or `$state.frozen` / a non-reactive `const` in the
75
76
  component. Reactive recompute of columns is rare and almost always a bug.
76
77
  - **`data`**: pass with a getter (`get data()`) so the reference is stable when the data
@@ -80,8 +81,10 @@ run, the table re-syncs more than it needs to.
80
81
  ```svelte
81
82
  <script lang="ts">
82
83
  // module scope is fine in .svelte too, when truly static
83
- const features = tableFeatures({ rowPaginationFeature })
84
- const rowModels = { paginatedRowModel: createPaginatedRowModel() }
84
+ const features = tableFeatures({
85
+ rowPaginationFeature,
86
+ paginatedRowModel: createPaginatedRowModel(),
87
+ })
85
88
  const columns = columnHelper.columns([
86
89
  /* ... */
87
90
  ])
@@ -91,7 +94,6 @@ run, the table re-syncs more than it needs to.
91
94
 
92
95
  const table = createTable({
93
96
  features,
94
- rowModels,
95
97
  columns,
96
98
  get data() {
97
99
  return data
@@ -48,7 +48,7 @@ A table instance has three (and a half) state surfaces:
48
48
  - `table.state` — the value returned by the optional selector passed as the second argument to
49
49
  `createTable`. **Svelte-only surface.**
50
50
 
51
- The Svelte adapter installs `svelteReactivity()` as the `coreReativityFeature`:
51
+ The Svelte adapter installs `svelteReactivity()` as the `coreReactivityFeature`:
52
52
 
53
53
  | Core concept | Svelte binding |
54
54
  | ------------- | --------------- |
@@ -81,14 +81,13 @@ import {
81
81
  const features = tableFeatures({
82
82
  rowPaginationFeature,
83
83
  rowSortingFeature,
84
+ paginatedRowModel: createPaginatedRowModel(),
85
+ sortedRowModel: createSortedRowModel(),
86
+ sortFns,
84
87
  })
85
88
 
86
89
  const table = createTable({
87
90
  features,
88
- rowModels: {
89
- paginatedRowModel: createPaginatedRowModel(),
90
- sortedRowModel: createSortedRowModel(sortFns),
91
- },
92
91
  columns,
93
92
  get data() {
94
93
  return data
@@ -122,7 +121,6 @@ exposed as `table.state`. The default selector returns the full registered state
122
121
  const table = createTable(
123
122
  {
124
123
  features,
125
- rowModels: { paginatedRowModel: createPaginatedRowModel() },
126
124
  columns,
127
125
  get data() {
128
126
  return data
@@ -186,7 +184,6 @@ The default: set starting values, let the table own the rest. `initialState` als
186
184
  ```ts
187
185
  const table = createTable({
188
186
  features,
189
- rowModels: {},
190
187
  columns,
191
188
  get data() {
192
189
  return data
@@ -231,7 +228,6 @@ const pagination = useSelector(paginationAtom)
231
228
 
232
229
  const table = createTable({
233
230
  features,
234
- rowModels: {},
235
231
  columns,
236
232
  get data() {
237
233
  return data
@@ -266,7 +262,6 @@ updates.
266
262
 
267
263
  const table = createTable({
268
264
  features,
269
- rowModels: {},
270
265
  columns,
271
266
  get data() {
272
267
  return data
@@ -340,8 +335,7 @@ Always key `{#each}` blocks on stable ids (`headerGroup.id`, `header.id`, `row.i
340
335
 
341
336
  ## `createTableHook` — app-wide composition
342
337
 
343
- Create one configured hook per app: shared `features`, `rowModels`, defaults, and pre-bound
344
- component registries.
338
+ Create one configured hook per app: shared `features` (including row-model factories), defaults, and pre-bound component registries.
345
339
 
346
340
  ```ts
347
341
  import {
@@ -363,11 +357,13 @@ export const {
363
357
  useCellContext,
364
358
  useHeaderContext,
365
359
  } = createTableHook({
366
- features: tableFeatures({ rowPaginationFeature, rowSortingFeature }),
367
- rowModels: {
360
+ features: tableFeatures({
361
+ rowPaginationFeature,
362
+ rowSortingFeature,
368
363
  paginatedRowModel: createPaginatedRowModel(),
369
- sortedRowModel: createSortedRowModel(sortFns),
370
- },
364
+ sortedRowModel: createSortedRowModel(),
365
+ sortFns,
366
+ }),
371
367
  cellComponents: { TextCell },
372
368
  headerComponents: { SortIndicator },
373
369
  })
@@ -51,7 +51,6 @@ export type SvelteTable<
51
51
  * const table = createTable(
52
52
  * {
53
53
  * features,
54
- * rowModels: {},
55
54
  * columns,
56
55
  * data,
57
56
  * },
@@ -73,7 +72,7 @@ export function createTable<
73
72
  // 1. Merge reactivity into options using mergeObjects (preserves getters)
74
73
  const mergedOptions = mergeObjects(tableOptions, {
75
74
  features: {
76
- coreReativityFeature: svelteReactivity(),
75
+ coreReactivityFeature: svelteReactivity(),
77
76
  ...tableOptions.features,
78
77
  },
79
78
  }) as TableOptions<TFeatures, TData>
@@ -376,6 +376,84 @@ export type AppSvelteTable<
376
376
  FlexRender: typeof FlexRenderSvelte
377
377
  }
378
378
 
379
+ export interface CreateTableHookResult<
380
+ TFeatures extends TableFeatures,
381
+ TTableComponents extends Record<string, ComponentType<any>>,
382
+ TCellComponents extends Record<string, ComponentType<any>>,
383
+ THeaderComponents extends Record<string, ComponentType<any>>,
384
+ > {
385
+ /** The features object that was passed to `createTableHook`. */
386
+ appFeatures: TFeatures
387
+ /**
388
+ * A column helper pre-bound to `TFeatures` and the registered components, so
389
+ * the cell/header/footer render props expose the bound components.
390
+ */
391
+ createAppColumnHelper: <TData extends RowData>() => AppColumnHelper<
392
+ TFeatures,
393
+ TData,
394
+ TCellComponents,
395
+ THeaderComponents
396
+ >
397
+ /**
398
+ * Creates a table with the `App*` wrapper components and registered
399
+ * `tableComponents` attached. `TData` is inferred from the `data` option.
400
+ */
401
+ createAppTable: <TData extends RowData, TSelected = TableState<TFeatures>>(
402
+ tableOptions: Omit<TableOptions<TFeatures, TData>, 'features'>,
403
+ selector?: (state: TableState<TFeatures>) => TSelected,
404
+ ) => AppSvelteTable<
405
+ TFeatures,
406
+ TData,
407
+ TSelected,
408
+ TTableComponents,
409
+ TCellComponents,
410
+ THeaderComponents
411
+ >
412
+ /**
413
+ * Reads the table provided by the nearest `<table.AppTable>`. This is the same
414
+ * extended instance `createAppTable` returns, so the `App*` components and your
415
+ * `tableComponents` are available on it.
416
+ *
417
+ * Pass `TSelected` to match the selector you gave `createAppTable`, so
418
+ * `table.state` is typed as the selected slice. It cannot be inferred
419
+ * automatically (context does not carry the provider's generics), so it
420
+ * defaults to the full table state, which is correct for the common case of
421
+ * `createAppTable` without a selector.
422
+ */
423
+ useTableContext: <
424
+ TData extends RowData = RowData,
425
+ TSelected = TableState<TFeatures>,
426
+ >() => AppSvelteTable<
427
+ TFeatures,
428
+ TData,
429
+ TSelected,
430
+ TTableComponents,
431
+ TCellComponents,
432
+ THeaderComponents
433
+ >
434
+ /**
435
+ * Reads the cell provided by the nearest `<table.AppCell>`, extended with your
436
+ * `cellComponents` and a context-bound `FlexRender`.
437
+ */
438
+ useCellContext: <TValue extends CellData = CellData>() => Cell<
439
+ TFeatures,
440
+ any,
441
+ TValue
442
+ > &
443
+ TCellComponents & { FlexRender: typeof FlexRenderSvelte }
444
+ /**
445
+ * Reads the header provided by the nearest `<table.AppHeader>` /
446
+ * `<table.AppFooter>`, extended with your `headerComponents` and a
447
+ * context-bound `FlexRender`.
448
+ */
449
+ useHeaderContext: <TValue extends CellData = CellData>() => Header<
450
+ TFeatures,
451
+ any,
452
+ TValue
453
+ > &
454
+ THeaderComponents & { FlexRender: typeof FlexRenderSvelte }
455
+ }
456
+
379
457
  // =============================================================================
380
458
  // createTableHook Factory
381
459
  // =============================================================================
@@ -404,12 +482,12 @@ export type AppSvelteTable<
404
482
  * rowPaginationFeature,
405
483
  * rowSortingFeature,
406
484
  * columnFilteringFeature,
407
- * }),
408
- * rowModels: {
409
485
  * paginatedRowModel: createPaginatedRowModel(),
410
- * sortedRowModel: createSortedRowModel(sortFns),
411
- * filteredRowModel: createFilteredRowModel(filterFns),
412
- * },
486
+ * sortedRowModel: createSortedRowModel(),
487
+ * filteredRowModel: createFilteredRowModel(),
488
+ * sortFns,
489
+ * filterFns,
490
+ * }),
413
491
  * tableComponents: { PaginationControls, RowCount },
414
492
  * cellComponents: { TextCell, NumberCell },
415
493
  * headerComponents: { SortIndicator, ColumnFilter },
@@ -431,7 +509,12 @@ export function createTableHook<
431
509
  TTableComponents,
432
510
  TCellComponents,
433
511
  THeaderComponents
434
- >) {
512
+ >): CreateTableHookResult<
513
+ TFeatures,
514
+ TTableComponents,
515
+ TCellComponents,
516
+ THeaderComponents
517
+ > {
435
518
  /**
436
519
  * Create a column helper pre-bound to the features and components configured in this table hook.
437
520
  * The cell, header, and footer contexts include pre-bound components (e.g., `cell.TextCell`).
@@ -455,9 +538,16 @@ export function createTableHook<
455
538
  * Use this in custom `tableComponents` passed to `createTableHook`.
456
539
  * TFeatures is already known from the createTableHook call.
457
540
  */
458
- function useTableContext<TData extends RowData = RowData>(): SvelteTable<
541
+ function useTableContext<
542
+ TData extends RowData = RowData,
543
+ TSelected = TableState<TFeatures>,
544
+ >(): AppSvelteTable<
459
545
  TFeatures,
460
- TData
546
+ TData,
547
+ TSelected,
548
+ TTableComponents,
549
+ TCellComponents,
550
+ THeaderComponents
461
551
  > {
462
552
  const table = getContext(tableContextKey)
463
553
 
@@ -468,7 +558,17 @@ export function createTableHook<
468
558
  )
469
559
  }
470
560
 
471
- return table as SvelteTable<TFeatures, TData>
561
+ // `<table.AppTable>` provides the extended table (the App* wrapper
562
+ // components and `tableComponents` are Object.assign-ed onto the same
563
+ // instance `createAppTable` returns), so this asserts the runtime shape.
564
+ return table as unknown as AppSvelteTable<
565
+ TFeatures,
566
+ TData,
567
+ TSelected,
568
+ TTableComponents,
569
+ TCellComponents,
570
+ THeaderComponents
571
+ >
472
572
  }
473
573
 
474
574
  /**
@@ -480,7 +580,8 @@ export function createTableHook<
480
580
  TFeatures,
481
581
  any,
482
582
  TValue
483
- > {
583
+ > &
584
+ TCellComponents & { FlexRender: typeof FlexRenderSvelte } {
484
585
  const cell = getContext(cellContextKey)
485
586
 
486
587
  if (!cell) {
@@ -490,7 +591,11 @@ export function createTableHook<
490
591
  )
491
592
  }
492
593
 
493
- return cell as Cell<TFeatures, any, TValue>
594
+ // `<table.AppCell>` Object.assign-es `cellComponents` and `FlexRender` onto
595
+ // the same cell instance it puts in context, so this asserts the runtime
596
+ // shape.
597
+ return cell as unknown as Cell<TFeatures, any, TValue> &
598
+ TCellComponents & { FlexRender: typeof FlexRenderSvelte }
494
599
  }
495
600
 
496
601
  /**
@@ -502,7 +607,8 @@ export function createTableHook<
502
607
  TFeatures,
503
608
  any,
504
609
  TValue
505
- > {
610
+ > &
611
+ THeaderComponents & { FlexRender: typeof FlexRenderSvelte } {
506
612
  const header = getContext(headerContextKey)
507
613
 
508
614
  if (!header) {
@@ -511,7 +617,10 @@ export function createTableHook<
511
617
  )
512
618
  }
513
619
 
514
- return header as Header<TFeatures, any, TValue>
620
+ // `<table.AppHeader>` / `<table.AppFooter>` Object.assign `headerComponents`
621
+ // and `FlexRender` onto the same header instance they put in context.
622
+ return header as unknown as Header<TFeatures, any, TValue> &
623
+ THeaderComponents & { FlexRender: typeof FlexRenderSvelte }
515
624
  }
516
625
 
517
626
  /**
@@ -527,10 +636,7 @@ export function createTableHook<
527
636
  TData extends RowData,
528
637
  TSelected = TableState<TFeatures>,
529
638
  >(
530
- tableOptions: Omit<
531
- TableOptions<TFeatures, TData>,
532
- 'features' | 'rowModels'
533
- >,
639
+ tableOptions: Omit<TableOptions<TFeatures, TData>, 'features'>,
534
640
  selector?: (state: TableState<TFeatures>) => TSelected,
535
641
  ): AppSvelteTable<
536
642
  TFeatures,
@@ -629,7 +735,7 @@ export function createTableHook<
629
735
  }
630
736
 
631
737
  return {
632
- appFeatures: defaultTableOptions.features as TFeatures,
738
+ appFeatures: defaultTableOptions.features,
633
739
  createAppColumnHelper,
634
740
  createAppTable,
635
741
  useTableContext,
package/src/index.ts CHANGED
@@ -14,6 +14,7 @@ export type {
14
14
  AppSvelteTable,
15
15
  ComponentType,
16
16
  CreateTableHookOptions,
17
+ CreateTableHookResult,
17
18
  } from './createTableHook.svelte'
18
19
  export { createTableState } from './createTableState.svelte'
19
20
  export { default as FlexRender } from './FlexRender.svelte'