From f27c4929e5f8376520f869715c19b4cdcf95c267 Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Wed, 29 Jul 2026 20:03:34 -0500 Subject: [PATCH 1/3] feat: remove svelte-table selectors to be inline with other signal-based adapters --- docs/config.json | 1 - docs/framework/svelte/guide/cell-selection.md | 36 ++- .../svelte/guide/column-filtering.md | 7 +- .../framework/svelte/guide/column-resizing.md | 4 +- .../svelte/guide/composable-tables.md | 10 +- .../framework/svelte/guide/custom-features.md | 4 +- .../svelte/guide/global-filtering.md | 6 +- docs/framework/svelte/guide/migrating.md | 106 ++++---- docs/framework/svelte/guide/pagination.md | 8 +- docs/framework/svelte/guide/row-selection.md | 13 +- docs/framework/svelte/guide/sorting.md | 7 +- docs/framework/svelte/guide/table-state.md | 254 ++++++++++-------- docs/framework/svelte/quick-start.md | 16 +- .../svelte/reference/functions/createTable.md | 39 ++- .../reference/functions/createTableHook.md | 2 +- .../reference/functions/subscribeTable.md | 112 -------- docs/framework/svelte/reference/index.md | 2 - .../interfaces/CreateTableHookResult.md | 44 ++- .../reference/type-aliases/AppCellContext.md | 14 +- .../type-aliases/AppColumnDefBase.md | 2 +- .../type-aliases/AppColumnDefTemplate.md | 2 +- .../reference/type-aliases/AppColumnHelper.md | 10 +- .../type-aliases/AppDisplayColumnDef.md | 2 +- .../type-aliases/AppGroupColumnDef.md | 2 +- .../type-aliases/AppHeaderContext.md | 8 +- .../reference/type-aliases/AppSvelteTable.md | 11 +- .../reference/type-aliases/ComponentType.md | 2 +- .../type-aliases/CreateTableHookOptions.md | 2 +- .../reference/type-aliases/SubscribeSource.md | 22 -- .../reference/type-aliases/SvelteTable.md | 45 +--- examples/svelte/aggregation/src/App.svelte | 11 +- .../svelte/basic-app-table/src/App.svelte | 2 +- .../basic-dynamic-columns/src/App.svelte | 1 - .../basic-external-atoms/src/App.svelte | 2 +- .../basic-external-state/src/App.svelte | 2 +- examples/svelte/cell-selection/src/App.svelte | 13 +- .../svelte/column-ordering/src/App.svelte | 2 +- .../column-pinning-split/src/App.svelte | 3 +- .../column-pinning-sticky/src/App.svelte | 3 +- examples/svelte/column-pinning/src/App.svelte | 2 +- .../svelte/column-resizing/src/App.svelte | 5 +- examples/svelte/column-sizing/src/App.svelte | 5 +- .../svelte/column-visibility/src/App.svelte | 2 +- .../column-visibility/tests/e2e/smoke.spec.ts | 2 +- .../src/components/PaginationControls.svelte | 7 +- .../src/components/ProductsTable.svelte | 15 +- .../src/components/UsersTable.svelte | 17 +- .../composable-tables/tests/e2e/smoke.spec.ts | 35 +++ examples/svelte/expanding/src/App.svelte | 16 +- examples/svelte/filtering/src/App.svelte | 13 +- .../filters-faceted-bucketed/src/App.svelte | 10 +- .../svelte/filters-faceted/src/App.svelte | 13 +- examples/svelte/filters-fuzzy/src/App.svelte | 17 +- .../svelte/grouped-aggregation/src/App.svelte | 10 +- examples/svelte/grouping/src/App.svelte | 10 +- examples/svelte/kitchen-sink/src/App.svelte | 16 +- examples/svelte/pagination/src/App.svelte | 10 +- .../svelte/pagination/tests/e2e/smoke.spec.ts | 42 +++ examples/svelte/row-pinning/src/App.svelte | 16 +- examples/svelte/row-selection/src/App.svelte | 22 +- .../row-selection/tests/e2e/smoke.spec.ts | 21 ++ examples/svelte/sorting/src/App.svelte | 3 +- examples/svelte/sub-components/src/App.svelte | 1 - .../svelte/with-tanstack-form/src/App.svelte | 22 +- .../src/PaginationControls.svelte | 7 +- .../svelte/with-tanstack-query/src/App.svelte | 3 +- examples/vue/pagination/src/App.vue | 2 - knip.json | 3 +- .../skills/migrate-v8-to-v9/SKILL.md | 9 +- .../svelte-table/skills/table-state/SKILL.md | 91 ++++--- .../svelte-table/src/createTable.svelte.ts | 79 ++---- .../src/createTableHook.svelte.ts | 47 +--- packages/svelte-table/src/index.ts | 1 - .../svelte-table/src/reactivity.svelte.ts | 7 +- packages/svelte-table/src/subscribe.ts | 46 ---- .../tests/adapter-lifecycle.test.ts | 52 ++-- .../tests/fixtures/HookHarness.svelte | 2 +- .../tests/fixtures/ReactivityHarness.svelte | 23 +- .../tests/fixtures/RuneStateHarness.svelte | 51 ++++ .../tests/fixtures/SelectorHarness.svelte | 47 ---- .../tests/fixtures/SsrHarness.svelte | 2 +- .../svelte-table/tests/public-api.types.ts | 54 ++++ packages/svelte-table/tests/rendering.test.ts | 3 +- perf-done.md | 40 ++- perf-todo.md | 37 +-- 85 files changed, 815 insertions(+), 955 deletions(-) delete mode 100644 docs/framework/svelte/reference/functions/subscribeTable.md delete mode 100644 docs/framework/svelte/reference/type-aliases/SubscribeSource.md delete mode 100644 packages/svelte-table/src/subscribe.ts create mode 100644 packages/svelte-table/tests/fixtures/RuneStateHarness.svelte delete mode 100644 packages/svelte-table/tests/fixtures/SelectorHarness.svelte create mode 100644 packages/svelte-table/tests/public-api.types.ts diff --git a/docs/config.json b/docs/config.json index c4db3fdcc0..966d3ca75c 100644 --- a/docs/config.json +++ b/docs/config.json @@ -590,7 +590,6 @@ { "label": "createTableState", "to": "framework/svelte/reference/functions/createTableState" }, { "label": "SvelteTable", "to": "framework/svelte/reference/type-aliases/SvelteTable" }, { "label": "AppSvelteTable", "to": "framework/svelte/reference/type-aliases/AppSvelteTable" }, - { "label": "subscribeTable", "to": "framework/svelte/reference/functions/subscribeTable" }, { "label": "renderComponent", "to": "framework/svelte/reference/functions/renderComponent" }, { "label": "renderSnippet", "to": "framework/svelte/reference/functions/renderSnippet" }, { "label": "FlexRender", "to": "framework/svelte/reference/variables/FlexRender" } diff --git a/docs/framework/svelte/guide/cell-selection.md b/docs/framework/svelte/guide/cell-selection.md index 949b9d8de6..03e47b3681 100644 --- a/docs/framework/svelte/guide/cell-selection.md +++ b/docs/framework/svelte/guide/cell-selection.md @@ -38,20 +38,27 @@ The cell selection feature keeps track of spreadsheet-style rectangular selectio The table instance already manages the cell selection state for you. You can access the selection or values derived from it through a few APIs. -- `table.state.cellSelection` - returns the cell selection state reactively (selected by the `createTable` selector) +- `table.atoms.cellSelection.get()` - returns the current cell selection state and participates in Svelte tracking when read in a template or rune - `getSelectedCellCount()` - returns how many cells are selected - `getSelectedCellIds()` - returns the ids of every selected cell - `getCellSelectionRowIds()` / `getCellSelectionColumnIds()` - returns the rows and columns the selection touches - `getSelectedCellRangesData()` - returns each selected rectangle's values as a row-major grid ```ts -console.log(table.state.cellSelection) //get the cell selection state +console.log(table.atoms.cellSelection.get()) //get the cell selection state console.log(table.getSelectedCellCount()) //3 console.log(table.getSelectedCellIds()) //['0_firstName', '0_lastName', '1_firstName'] console.log(table.getSelectedCellRangesData()) //[[['Tanner', 'Linsley'], ['Kevin', 'Vandy']]] ``` -In event handlers or other non-render code, you can also read the current snapshot with `table.atoms.cellSelection.get()`. This read does not subscribe a component to future changes, so prefer `table.state.cellSelection` in render positions. +Use the same atom read in markup or a native derived value: + +```ts +const cellSelection = $derived(table.atoms.cellSelection.get()) +const selectedRangeCount = $derived(cellSelection.length) +``` + +Outside a tracked context, `table.atoms.cellSelection.get()` is simply the current snapshot. The expansion APIs (`getSelectedCellIds`, `getSelectedCellRangesData`) are memoized and pull-based. They cost nothing unless you actually call them, so a table that only highlights cells never pays to enumerate a large selection. @@ -313,8 +320,8 @@ Because a reorder can widen a selection onto columns the user never picked, some let isFirstColumnLayout = true $effect(() => { - // read the atoms, not table.state: the selector rebuilds that object on every - // selected slice change, so reading it here would re-fire on every selection + // Read only the layout atoms. A full table.store.get() read would re-run for + // every table state change, including selection changes. void table.atoms.columnOrder.get() void table.atoms.columnPinning.get() void table.atoms.columnVisibility.get() @@ -343,21 +350,8 @@ const table = createTable({ ### Performance -Svelte's runes track these reads and the compiler updates only the DOM nodes -that actually changed, so the example renders its cells plainly. +Svelte's runes track atom reads and the compiler updates only the DOM nodes that actually changed, so the example renders its cells plainly. -Measured on a table with a thousand rows and twelve columns, a drag updates in -roughly 16ms per move with plain reads. - -`subscribeTable` is available if you want an explicit fine-grained subscription: - -```svelte -const selected = subscribeTable( table.atoms.cellSelection, (ranges) => -ranges.length, ) // read it as selected.current -``` +Measured on a table with a thousand rows and twelve columns, a drag updates in roughly 16ms per move with plain reads. -One trap worth knowing: do not read `table.state` inside an `$effect` that also -writes selection state. The selector rebuilds that object whenever any selected -slice changes, so an effect meant to watch the column layout will re-fire on -every selection change and clear the selection you just made. Read -`table.atoms..get()` instead. +One trap worth knowing: do not read `table.store.get()` inside an `$effect` that also writes selection state unless the effect truly depends on every state slice. An effect meant to watch column layout would otherwise re-run on every selection change and clear the selection you just made. Read only the required `table.atoms..get()` values instead. diff --git a/docs/framework/svelte/guide/column-filtering.md b/docs/framework/svelte/guide/column-filtering.md index 4240a13eb3..fdd303d8f6 100644 --- a/docs/framework/svelte/guide/column-filtering.md +++ b/docs/framework/svelte/guide/column-filtering.md @@ -138,7 +138,7 @@ Since the column filter state is an array of objects, you can have multiple colu #### Accessing Column Filter State -For reactive reads that should update your UI, use `table.state.columnFilters` (selected by the `createTable` selector) or `subscribeTable`. In event handlers or other non-reactive code, you can read the current snapshot with `table.atoms.columnFilters.get()`, but this read only participates in Svelte dependency tracking when called in a rune-tracked context. +Read column filters from `table.atoms.columnFilters.get()`. The call returns the current value in event handlers and becomes reactive when it runs in a template, `$derived`, `$derived.by`, or `$effect`. ```ts const table = createTable({ @@ -148,8 +148,9 @@ const table = createTable({ //... }) -table.state.columnFilters // reactive read -table.atoms.columnFilters.get() // snapshot read in event handlers +const columnFilters = $derived(table.atoms.columnFilters.get()) + +table.atoms.columnFilters.get() // current snapshot in an event handler ``` However, if you need access to the column filter state outside of the table, you can "control" the column filter state like down below. diff --git a/docs/framework/svelte/guide/column-resizing.md b/docs/framework/svelte/guide/column-resizing.md index 13e78f2bd3..1b14e6fbc2 100644 --- a/docs/framework/svelte/guide/column-resizing.md +++ b/docs/framework/svelte/guide/column-resizing.md @@ -148,7 +148,7 @@ TanStack Table keeps track of a `columnResizing` state object that you can use t
``` @@ -253,4 +253,4 @@ Svelte 5's fine-grained reactivity means a resize does not re-render whole compo 1. **Derive all column widths in one `$derived` value.** Build a single style string that maps each header and column id to a CSS variable, then bind it on the `` element (`
`). `header.getSize()` reads the rune-backed `columnSizing` atom, so a drag re-runs only this derived and its single attribute effect. 2. **Reference the variables in cell styles** (`width: calc(var(--col-firstName-size) * 1px)`) so the browser applies new widths without Svelte re-evaluating each cell. -Because nothing in the table body reads resize state, no body memoization is needed, and you do not need a `createTable` selector for this pattern; the resizer's highlight is its own inline binding. +Because nothing in the table body reads resize state, no body memoization is needed; the resizer's highlight is its own narrow inline atom binding. diff --git a/docs/framework/svelte/guide/composable-tables.md b/docs/framework/svelte/guide/composable-tables.md index 584637319f..e126d0602e 100644 --- a/docs/framework/svelte/guide/composable-tables.md +++ b/docs/framework/svelte/guide/composable-tables.md @@ -276,13 +276,9 @@ Create each table with `createAppTable`. In Svelte 5, pass reactive data through debugTable: true, }) - let sorting = $derived(table.state.sorting) - let columnFilters = $derived(table.state.columnFilters) - - const rows = $derived.by(() => { - JSON.stringify(table.state) - return table.getRowModel().rows - }) + const sorting = $derived(table.atoms.sorting.get()) + const columnFilters = $derived(table.atoms.columnFilters.get()) + const rows = $derived(table.getRowModel().rows) ``` diff --git a/docs/framework/svelte/guide/custom-features.md b/docs/framework/svelte/guide/custom-features.md index 509d91c658..f2afbb548e 100644 --- a/docs/framework/svelte/guide/custom-features.md +++ b/docs/framework/svelte/guide/custom-features.md @@ -330,10 +330,10 @@ const table = createTable({ ### Step 5: Use the Feature in Your Application -Now that the feature is added to the table instance, you can use the new instance APIs, options, and state in your application. `table.state.density` reads the new state slice reactively in your markup, and `table.setDensity` / `table.toggleDensity` update it. +Now that the feature is added to the table instance, you can use the new instance APIs, options, and state in your application. `table.atoms.density.get()` reads the new state slice reactively in your markup, and `table.setDensity` / `table.toggleDensity` update it. ```svelte -{@const density = table.state.density} +{@const density = table.atoms.density.get()}
table.setGlobalFilter((e.target as HTMLInputElement).value)} placeholder="Search all columns..." /> diff --git a/docs/framework/svelte/guide/migrating.md b/docs/framework/svelte/guide/migrating.md index 030ab394a9..de52651f3e 100644 --- a/docs/framework/svelte/guide/migrating.md +++ b/docs/framework/svelte/guide/migrating.md @@ -2,6 +2,9 @@ title: Migrating to TanStack Table V9 (Svelte) --- +> [!NOTE] +> `v9.0.0-beta.59` removes Svelte's table-creation selectors. `createTable` and `createAppTable` now accept only their options object; the selected `table.state` property, `subscribeTable`, and `SubscribeSource` are also removed. Read one slice with `table.atoms..get()` and the complete state with `table.store.get()`. These reads update naturally in Svelte templates, `$derived`, `$derived.by`, and `$effect`. This is an intentional beta breaking change with no compatibility overload or runtime shim. See [State Management Changes](#state-management-changes) for examples. + > [!NOTE] > `v9.0.0-beta.48`/`beta.49` split aggregation out of `columnGroupingFeature` into a new `rowAggregationFeature` (`stockFeatures` includes both). If you declare features explicitly, add `rowAggregationFeature` anywhere you use `aggregationFns`, `aggregationFn`, `aggregatedCell`, `cell.getIsAggregated()`, or `column.getAggregationValue()`. Aggregation function definitions, row-depth selection, and the `getAggregationValue` signature also changed. See [Grouping and Aggregation](#grouping-and-aggregation) below. @@ -25,7 +28,7 @@ TanStack Table V9 is a major release with significant internal architectural imp - **TanStack Store foundation**: Table state is backed by TanStack Store atoms. - **Svelte 5 reactivity**: The adapter uses Svelte 5 runes and Svelte-aware atom bindings. -- **Fine-grained subscriptions**: `table.state` contains the full registered state by default; use a custom selector, `subscribeTable`, or `useSelector` from `@tanstack/svelte-store` when you need a narrower reactive surface. +- **Native fine-grained reads**: Use `table.atoms..get()` for narrow state and native `$derived` values for projections. Use `table.store.get()` when a computation intentionally needs the complete state. ### 3. Type-Safety Improvements @@ -314,13 +317,11 @@ Because these methods now live on the prototype, they also do not appear as own Svelte v9 table state is atom-backed and rune-aware. Do not port v8 writable-store table option patterns directly except as "before" code. -| Surface | Use | -| ----------------------------------- | ---------------------------------------------------------------------------------------------------- | -| `table.state` | Full registered table state by default, or selected state from the second argument to `createTable`. | -| `table.store.state` | Current full table state snapshot. | -| `table.atoms..get()` | Narrow current-value read for one state slice. | -| `subscribeTable(source, selector?)` | Fine-grained Svelte subscription helper that exposes `.current`. | -| `table.baseAtoms.` | Internal writable atoms. Prefer feature APIs or external atoms. | +| Surface | Use | +| --------------------------- | ------------------------------------------------------------------------------- | +| `table.atoms..get()` | Narrow, rune-aware read for one registered state slice. | +| `table.store.get()` | Rune-aware full-state read; intentionally updates for any table state change. | +| `table.baseAtoms.` | Internal writable atoms. Prefer feature APIs or externally owned state instead. | ### Accessing State @@ -328,69 +329,73 @@ Svelte v9 table state is atom-backed and rune-aware. Do not port v8 writable-sto // v8 const sorting = table.getState().sorting -// v9: full snapshot -const sorting = table.store.state.sorting - -// v9: narrow atom read +// v9: narrow read (preferred for one slice) const sorting = table.atoms.sorting.get() -``` - -By default, `table.state` contains the full registered table state. -```ts -const table = createTable({ - features, - columns, - get data() { - return data - }, -}) - -const { pagination, sorting } = table.state +// v9: complete state +const state = table.store.get() ``` -Pass a second-argument selector when you want `table.state` to contain only the values that should cause reactive updates. +The same reads become reactive dependencies when they run in a Svelte template, `$derived`, `$derived.by`, or `$effect`: -```ts -const table = createTable( - { +```svelte + + +Page {pagination.pageIndex + 1} ``` -Passing `(state) => state` is equivalent to the default selector and is no longer necessary. +`$derived` now owns projection and equality behavior. A full-store read is appropriate for debug output, persistence, or computations that intentionally depend on every slice; use an atom when only one slice matters. -### Fine-grained Updates with `subscribeTable` +#### Migrating a `createTable` Selector ```ts -import { subscribeTable } from '@tanstack/svelte-table' +// Before beta.59 +const table = createTable(options, (state) => ({ + pagination: state.pagination, +})) -const pagination = subscribeTable(table.atoms.pagination) -const pageIndex = subscribeTable( - table.atoms.pagination, - (pagination) => pagination.pageIndex, -) +table.state.pagination ``` -```svelte - - Page {pagination.current.pageIndex + 1} of {table.getPageCount()} - +```ts +// beta.59+ +const table = createTable(options) +const pagination = $derived(table.atoms.pagination.get()) ``` +Identity selectors and selectors returning `null` should simply be deleted. Atom and table API reads already register the relevant Svelte dependencies. + +#### Migrating a `createAppTable` Selector + +```ts +// Before beta.59 +const table = createAppTable(options, (state) => ({ + sorting: state.sorting, +})) +table.state.sorting + +// beta.59+ +const table = createAppTable(options) +const sorting = $derived(table.atoms.sorting.get()) +``` + +The selected-state generic was removed with the selector. `SvelteTable` now has two generic parameters, `AppSvelteTable` has five, and `useTableContext` only accepts its optional row-data generic. + ### Controlled State -The v8-style `state` plus `on[State]Change` pattern still works in v9 and is the most direct migration path, but [External Atoms](#external-atoms) (below) are the preferred v9 way to own state slices outside the table. +The v8-style `state` plus `on[State]Change` pattern still works in v9 and is the most direct migration path. Prefer Svelte `$state` for state owned by a component. Use [External Atoms](#external-atoms) when the app must share raw TanStack Store atoms outside the table. Use `createTableState` for Svelte-owned state slices that need to accept TanStack Table updater functions. @@ -865,7 +870,10 @@ type Person = { - [ ] Convert custom aggregation callables to `constructAggregationFn({ aggregate, merge? })` definitions. - [ ] Replace Svelte 3/4 writable-store table patterns with runes and getters. - [ ] Pass reactive `data` and controlled `state` slices through getters. -- [ ] Replace `table.getState()` reads with `table.state`, `table.store.state`, `table.atoms..get()`, or `subscribeTable`. +- [ ] Remove second-argument selectors from `createTable` and `createAppTable`. +- [ ] Replace `table.state` and `table.getState()` reads with `table.atoms..get()` or `table.store.get()`. +- [ ] Replace selector projections with native `$derived` values. +- [ ] Remove `subscribeTable` / `SubscribeSource` imports and update `SvelteTable` / `AppSvelteTable` generic arity. - [ ] Replace `onStateChange` with per-slice callbacks or external atoms. - [ ] Replace `declare module` augmentation of `FilterFns`/`SortFns`/`AggregationFns` with registry-slot registration, and `FilterMeta` augmentation with the `filterMeta` slot. - [ ] Add `typeof features` to column helpers and types. diff --git a/docs/framework/svelte/guide/pagination.md b/docs/framework/svelte/guide/pagination.md index 70386579c5..0463d37c37 100644 --- a/docs/framework/svelte/guide/pagination.md +++ b/docs/framework/svelte/guide/pagination.md @@ -126,7 +126,11 @@ The `pagination` state is an object that contains the following properties: - `pageIndex`: The current page index (zero-based). - `pageSize`: The current page size. -For reactive reads that should update your UI, use `table.state.pagination` (selected by the `createTable` selector). In event handlers, you can read the current snapshot with `table.atoms.pagination.get()`, but this read only participates in Svelte dependency tracking when called in a rune-tracked context. +Read pagination from `table.atoms.pagination.get()`. The call returns the current value in event handlers and becomes reactive when it runs in a template, `$derived`, `$derived.by`, or `$effect`. + +```ts +const pagination = $derived(table.atoms.pagination.get()) +``` If you need access to the `pagination` state outside of the table (a server-side query key is the most common case), you can own the slice yourself. The recommended way in v9 is an external atom passed through the `atoms` table option. Atoms preserve fine-grained subscriptions, and the pagination value can be used elsewhere (such as in a query key) without coupling that code to the table instance. @@ -258,7 +262,7 @@ There are several pagination table instance APIs that are useful for hooking up diff --git a/docs/framework/svelte/reference/functions/createTable.md b/docs/framework/svelte/reference/functions/createTable.md index 7742024b2a..b5fb364aaf 100644 --- a/docs/framework/svelte/reference/functions/createTable.md +++ b/docs/framework/svelte/reference/functions/createTable.md @@ -6,17 +6,19 @@ title: createTable # Function: createTable() ```ts -function createTable(tableOptions, selector?): SvelteTable; +function createTable(tableOptions): SvelteTable; ``` -Defined in: [packages/svelte-table/src/createTable.svelte.ts:64](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTable.svelte.ts#L64) +Defined in: [packages/svelte-table/src/createTable.svelte.ts:46](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTable.svelte.ts#L46) Creates a Svelte 5 table instance backed by rune-aware TanStack Store atoms. -The optional selector projects from `table.store`; the selected value is -exposed on `table.state`. The adapter syncs options in `$effect.pre`, so -reactive option getters and external `$state` values are applied before DOM -updates read table APIs such as `getRowModel()`. +Read a specific state slice with `table.atoms..get()` and read the +complete state with `table.store.get()`. Those reads participate in Svelte +dependency tracking when they run in a template, `$derived`, or `$effect`. +The adapter syncs options in `$effect.pre`, so reactive option getters and +external `$state` values are applied before DOM updates read table APIs such +as `getRowModel()`. ## Type Parameters @@ -28,37 +30,26 @@ updates read table APIs such as `getRowModel()`. `TData` *extends* `RowData` -### TSelected - -`TSelected` = `TableState`\<`TFeatures`\> - ## Parameters ### tableOptions `TableOptions`\<`TFeatures`, `TData`\> -### selector? - -(`state`) => `TSelected` - ## Returns -[`SvelteTable`](../type-aliases/SvelteTable.md)\<`TFeatures`, `TData`, `TSelected`\> +[`SvelteTable`](../type-aliases/SvelteTable.md)\<`TFeatures`, `TData`\> ## Example ```svelte -{table.state.pagination.pageIndex} +Page {pagination.pageIndex + 1} +
{stateJson}
``` diff --git a/docs/framework/svelte/reference/functions/createTableHook.md b/docs/framework/svelte/reference/functions/createTableHook.md index a8eb7ea0a3..78594cee10 100644 --- a/docs/framework/svelte/reference/functions/createTableHook.md +++ b/docs/framework/svelte/reference/functions/createTableHook.md @@ -9,7 +9,7 @@ title: createTableHook function createTableHook(__namedParameters): CreateTableHookResult; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:497](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L497) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:488](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L488) Creates a custom table hook with pre-bound components for composition. diff --git a/docs/framework/svelte/reference/functions/subscribeTable.md b/docs/framework/svelte/reference/functions/subscribeTable.md deleted file mode 100644 index d13d94c23f..0000000000 --- a/docs/framework/svelte/reference/functions/subscribeTable.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -id: subscribeTable -title: subscribeTable ---- - -# Function: subscribeTable() - -## Call Signature - -```ts -function subscribeTable(source): object; -``` - -Defined in: [packages/svelte-table/src/subscribe.ts:34](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/subscribe.ts#L34) - -Creates a fine-grained Svelte subscription to a TanStack Store source. - -Pass a table atom or store and optionally project it with a selector. The -returned selector store exposes `.current`, making it useful for reading -focused table state outside the broad `createTable` selector. - -### Type Parameters - -#### TSourceValue - -`TSourceValue` - -### Parameters - -#### source - -[`SubscribeSource`](../type-aliases/SubscribeSource.md)\<`TSourceValue`\> - -### Returns - -`object` - -#### current - -```ts -readonly current: NoInfer; -``` - -### Example - -```svelte - - - -``` - -## Call Signature - -```ts -function subscribeTable(source, selector): object; -``` - -Defined in: [packages/svelte-table/src/subscribe.ts:37](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/subscribe.ts#L37) - -Creates a fine-grained Svelte subscription to a TanStack Store source. - -Pass a table atom or store and optionally project it with a selector. The -returned selector store exposes `.current`, making it useful for reading -focused table state outside the broad `createTable` selector. - -### Type Parameters - -#### TSourceValue - -`TSourceValue` - -#### TSelected - -`TSelected` - -### Parameters - -#### source - -[`SubscribeSource`](../type-aliases/SubscribeSource.md)\<`TSourceValue`\> - -#### selector - -(`state`) => `TSelected` - -### Returns - -`object` - -#### current - -```ts -readonly current: TSelected; -``` - -### Example - -```svelte - - - -``` diff --git a/docs/framework/svelte/reference/index.md b/docs/framework/svelte/reference/index.md index 77c971d9bf..7d73aa4813 100644 --- a/docs/framework/svelte/reference/index.md +++ b/docs/framework/svelte/reference/index.md @@ -22,7 +22,6 @@ title: "@tanstack/svelte-table" - [ComponentType](type-aliases/ComponentType.md) - [CreateTableHookOptions](type-aliases/CreateTableHookOptions.md) - [FlexRender](type-aliases/FlexRender.md) -- [SubscribeSource](type-aliases/SubscribeSource.md) - [SvelteTable](type-aliases/SvelteTable.md) ## Variables @@ -36,4 +35,3 @@ title: "@tanstack/svelte-table" - [createTableState](functions/createTableState.md) - [renderComponent](functions/renderComponent.md) - [renderSnippet](functions/renderSnippet.md) -- [subscribeTable](functions/subscribeTable.md) diff --git a/docs/framework/svelte/reference/interfaces/CreateTableHookResult.md b/docs/framework/svelte/reference/interfaces/CreateTableHookResult.md index 9fb7672596..fc8d035e95 100644 --- a/docs/framework/svelte/reference/interfaces/CreateTableHookResult.md +++ b/docs/framework/svelte/reference/interfaces/CreateTableHookResult.md @@ -5,7 +5,7 @@ title: CreateTableHookResult # Interface: CreateTableHookResult\ -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:379](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L379) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:378](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L378) ## Type Parameters @@ -33,7 +33,7 @@ Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:379](https://gi appFeatures: TFeatures; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:386](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L386) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:385](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L385) The features object that was passed to `createTableHook`. @@ -45,7 +45,7 @@ The features object that was passed to `createTableHook`. createAppColumnHelper: () => AppColumnHelper; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:391](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L391) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:390](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L390) A column helper pre-bound to `TFeatures` and the registered components, so the cell/header/footer render props expose the bound components. @@ -65,37 +65,33 @@ the cell/header/footer render props expose the bound components. ### createAppTable() ```ts -createAppTable: (tableOptions, selector?) => AppSvelteTable; +createAppTable: (tableOptions) => AppSvelteTable; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:401](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L401) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:404](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L404) Creates a table with the `App*` wrapper components and registered `tableComponents` attached. `TData` is inferred from the `data` option. +Read table state with `table.atoms..get()` or `table.store.get()`. +These reads participate in Svelte dependency tracking inside templates, +`$derived`, and `$effect`. + #### Type Parameters ##### TData `TData` *extends* `RowData` -##### TSelected - -`TSelected` = `TableState`\<`TFeatures`\> - #### Parameters ##### tableOptions `Omit`\<`TableOptions`\<`TFeatures`, `TData`\>, `"features"`\> -##### selector? - -(`state`) => `TSelected` - #### Returns -[`AppSvelteTable`](../type-aliases/AppSvelteTable.md)\<`TFeatures`, `TData`, `TSelected`, `TTableComponents`, `TCellComponents`, `THeaderComponents`\> +[`AppSvelteTable`](../type-aliases/AppSvelteTable.md)\<`TFeatures`, `TData`, `TTableComponents`, `TCellComponents`, `THeaderComponents`\> *** @@ -105,7 +101,7 @@ Creates a table with the `App*` wrapper components and registered useCellContext: () => Cell_Core & ExtractFeatureMapTypes & TCellComponents & object; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:438](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L438) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:429](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L429) Reads the cell provided by the nearest ``, extended with your `cellComponents` and a context-bound `FlexRender`. @@ -128,7 +124,7 @@ Reads the cell provided by the nearest ``, extended with your useHeaderContext: () => Header_Core & ExtractFeatureMapTypes & THeaderComponents & object; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:449](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L449) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:440](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L440) Reads the header provided by the nearest `` / ``, extended with your `headerComponents` and a @@ -149,31 +145,21 @@ context-bound `FlexRender`. ### useTableContext() ```ts -useTableContext: () => AppSvelteTable; +useTableContext: () => AppSvelteTable; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:423](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L423) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:418](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L418) Reads the table provided by the nearest ``. This is the same extended instance `createAppTable` returns, so the `App*` components and your `tableComponents` are available on it. -Pass `TSelected` to match the selector you gave `createAppTable`, so -`table.state` is typed as the selected slice. It cannot be inferred -automatically (context does not carry the provider's generics), so it -defaults to the full table state, which is correct for the common case of -`createAppTable` without a selector. - #### Type Parameters ##### TData `TData` *extends* `RowData` = `RowData` -##### TSelected - -`TSelected` = `TableState`\<`TFeatures`\> - #### Returns -[`AppSvelteTable`](../type-aliases/AppSvelteTable.md)\<`TFeatures`, `TData`, `TSelected`, `TTableComponents`, `TCellComponents`, `THeaderComponents`\> +[`AppSvelteTable`](../type-aliases/AppSvelteTable.md)\<`TFeatures`, `TData`, `TTableComponents`, `TCellComponents`, `THeaderComponents`\> diff --git a/docs/framework/svelte/reference/type-aliases/AppCellContext.md b/docs/framework/svelte/reference/type-aliases/AppCellContext.md index 6d45e9d72c..d08f34f33d 100644 --- a/docs/framework/svelte/reference/type-aliases/AppCellContext.md +++ b/docs/framework/svelte/reference/type-aliases/AppCellContext.md @@ -9,7 +9,7 @@ title: AppCellContext type AppCellContext = object; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:50](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L50) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:49](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L49) Enhanced CellContext with pre-bound cell components. The `cell` property includes the registered cellComponents. @@ -40,7 +40,7 @@ The `cell` property includes the registered cellComponents. cell: Cell & TCellComponents & object; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:56](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L56) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:55](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L55) #### Type Declaration @@ -58,7 +58,7 @@ FlexRender: typeof FlexRender; column: Column; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:58](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L58) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:57](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L57) *** @@ -68,7 +68,7 @@ Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:58](https://git getValue: CellContext["getValue"]; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:59](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L59) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:58](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L58) *** @@ -78,7 +78,7 @@ Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:59](https://git renderValue: CellContext["renderValue"]; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:60](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L60) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:59](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L59) *** @@ -88,7 +88,7 @@ Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:60](https://git row: Row; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:61](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L61) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:60](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L60) *** @@ -98,4 +98,4 @@ Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:61](https://git table: Table; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:62](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L62) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:61](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L61) diff --git a/docs/framework/svelte/reference/type-aliases/AppColumnDefBase.md b/docs/framework/svelte/reference/type-aliases/AppColumnDefBase.md index 90c1425ab3..02ab6b23f2 100644 --- a/docs/framework/svelte/reference/type-aliases/AppColumnDefBase.md +++ b/docs/framework/svelte/reference/type-aliases/AppColumnDefBase.md @@ -9,7 +9,7 @@ title: AppColumnDefBase type AppColumnDefBase = Omit, "cell" | "header" | "footer"> & object; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:95](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L95) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:94](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L94) Enhanced column definition base with pre-bound components in cell/header/footer contexts. diff --git a/docs/framework/svelte/reference/type-aliases/AppColumnDefTemplate.md b/docs/framework/svelte/reference/type-aliases/AppColumnDefTemplate.md index d4c0a30edc..8ed9fb67de 100644 --- a/docs/framework/svelte/reference/type-aliases/AppColumnDefTemplate.md +++ b/docs/framework/svelte/reference/type-aliases/AppColumnDefTemplate.md @@ -9,7 +9,7 @@ title: AppColumnDefTemplate type AppColumnDefTemplate = string | (props) => any; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:88](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L88) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:87](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L87) Template type for column definitions that can be a string or a function. diff --git a/docs/framework/svelte/reference/type-aliases/AppColumnHelper.md b/docs/framework/svelte/reference/type-aliases/AppColumnHelper.md index 89f3a980fe..eb917db1ec 100644 --- a/docs/framework/svelte/reference/type-aliases/AppColumnHelper.md +++ b/docs/framework/svelte/reference/type-aliases/AppColumnHelper.md @@ -9,7 +9,7 @@ title: AppColumnHelper type AppColumnHelper = object; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:171](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L171) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:170](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L170) Enhanced column helper with pre-bound components in cell/header/footer contexts. This enables TypeScript to know about the registered components when defining columns. @@ -40,7 +40,7 @@ This enables TypeScript to know about the registered components when defining co accessor: (accessor, column) => TAccessor extends AccessorFn ? AccessorFnColumnDef : AccessorKeyColumnDef; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:181](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L181) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:180](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L180) Creates a data column definition with an accessor key or function. The cell, header, and footer contexts include pre-bound components. @@ -77,7 +77,7 @@ The cell, header, and footer contexts include pre-bound components. columns: (columns) => ColumnDef[] & [...TColumns]; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:212](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L212) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:211](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L211) Wraps an array of column definitions to preserve each column's individual TValue type. @@ -105,7 +105,7 @@ Wraps an array of column definitions to preserve each column's individual TValue display: (column) => DisplayColumnDef; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:220](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L220) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:219](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L219) Creates a display column definition for non-data columns. The cell, header, and footer contexts include pre-bound components. @@ -128,7 +128,7 @@ The cell, header, and footer contexts include pre-bound components. group: (column) => GroupColumnDef; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:233](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L233) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:232](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L232) Creates a group column definition with nested child columns. The cell, header, and footer contexts include pre-bound components. diff --git a/docs/framework/svelte/reference/type-aliases/AppDisplayColumnDef.md b/docs/framework/svelte/reference/type-aliases/AppDisplayColumnDef.md index a37c325f25..ed1eb161d2 100644 --- a/docs/framework/svelte/reference/type-aliases/AppDisplayColumnDef.md +++ b/docs/framework/svelte/reference/type-aliases/AppDisplayColumnDef.md @@ -9,7 +9,7 @@ title: AppDisplayColumnDef type AppDisplayColumnDef = Omit, "cell" | "header" | "footer"> & object; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:119](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L119) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:118](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L118) Enhanced display column definition with pre-bound components. diff --git a/docs/framework/svelte/reference/type-aliases/AppGroupColumnDef.md b/docs/framework/svelte/reference/type-aliases/AppGroupColumnDef.md index 792e485695..b02d54e94c 100644 --- a/docs/framework/svelte/reference/type-aliases/AppGroupColumnDef.md +++ b/docs/framework/svelte/reference/type-aliases/AppGroupColumnDef.md @@ -9,7 +9,7 @@ title: AppGroupColumnDef type AppGroupColumnDef = Omit, "cell" | "header" | "footer" | "columns"> & object; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:142](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L142) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:141](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L141) Enhanced group column definition with pre-bound components. diff --git a/docs/framework/svelte/reference/type-aliases/AppHeaderContext.md b/docs/framework/svelte/reference/type-aliases/AppHeaderContext.md index 2c675a16ab..bda4ac0467 100644 --- a/docs/framework/svelte/reference/type-aliases/AppHeaderContext.md +++ b/docs/framework/svelte/reference/type-aliases/AppHeaderContext.md @@ -9,7 +9,7 @@ title: AppHeaderContext type AppHeaderContext = object; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:69](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L69) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:68](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L68) Enhanced HeaderContext with pre-bound header components. The `header` property includes the registered headerComponents. @@ -40,7 +40,7 @@ The `header` property includes the registered headerComponents. column: Column; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:75](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L75) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:74](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L74) *** @@ -50,7 +50,7 @@ Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:75](https://git header: Header & THeaderComponents & object; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:76](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L76) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:75](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L75) #### Type Declaration @@ -68,4 +68,4 @@ FlexRender: typeof FlexRender; table: Table; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:78](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L78) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:77](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L77) diff --git a/docs/framework/svelte/reference/type-aliases/AppSvelteTable.md b/docs/framework/svelte/reference/type-aliases/AppSvelteTable.md index 215397a913..97f2686f3e 100644 --- a/docs/framework/svelte/reference/type-aliases/AppSvelteTable.md +++ b/docs/framework/svelte/reference/type-aliases/AppSvelteTable.md @@ -3,15 +3,16 @@ id: AppSvelteTable title: AppSvelteTable --- -# Type Alias: AppSvelteTable\ +# Type Alias: AppSvelteTable\ ```ts -type AppSvelteTable = SvelteTable & NoInfer & object; +type AppSvelteTable = SvelteTable & NoInfer & object; ``` Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:290](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L290) -Extended table API returned by createAppTable with all App wrapper components. +Svelte-aware table returned by `createAppTable`, extended with the registered +table components and the `App*` context wrappers. ## Type Declaration @@ -114,10 +115,6 @@ Convenience FlexRender component attached to the table instance. `TData` *extends* `RowData` -### TSelected - -`TSelected` - ### TTableComponents `TTableComponents` *extends* `Record`\<`string`, [`ComponentType`](ComponentType.md)\<`any`\>\> diff --git a/docs/framework/svelte/reference/type-aliases/ComponentType.md b/docs/framework/svelte/reference/type-aliases/ComponentType.md index a6daf6ee15..eb16ee6eda 100644 --- a/docs/framework/svelte/reference/type-aliases/ComponentType.md +++ b/docs/framework/svelte/reference/type-aliases/ComponentType.md @@ -9,7 +9,7 @@ title: ComponentType type ComponentType = Component; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:40](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L40) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:39](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L39) ## Type Parameters diff --git a/docs/framework/svelte/reference/type-aliases/CreateTableHookOptions.md b/docs/framework/svelte/reference/type-aliases/CreateTableHookOptions.md index 193e0686b0..13c312a51b 100644 --- a/docs/framework/svelte/reference/type-aliases/CreateTableHookOptions.md +++ b/docs/framework/svelte/reference/type-aliases/CreateTableHookOptions.md @@ -9,7 +9,7 @@ title: CreateTableHookOptions type CreateTableHookOptions = Omit, "columns" | "data" | "store" | "state" | "initialState"> & object; ``` -Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:251](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L251) +Defined in: [packages/svelte-table/src/createTableHook.svelte.ts:250](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTableHook.svelte.ts#L250) Options for creating a table hook with pre-bound components and default table options. Extends all TableOptions except 'columns' | 'data' | 'store' | 'state' | 'initialState'. diff --git a/docs/framework/svelte/reference/type-aliases/SubscribeSource.md b/docs/framework/svelte/reference/type-aliases/SubscribeSource.md deleted file mode 100644 index ed5a22f01e..0000000000 --- a/docs/framework/svelte/reference/type-aliases/SubscribeSource.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -id: SubscribeSource -title: SubscribeSource ---- - -# Type Alias: SubscribeSource\ - -```ts -type SubscribeSource = - | Atom - | ReadonlyAtom - | Store -| ReadonlyStore; -``` - -Defined in: [packages/svelte-table/src/subscribe.ts:9](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/subscribe.ts#L9) - -## Type Parameters - -### TValue - -`TValue` diff --git a/docs/framework/svelte/reference/type-aliases/SvelteTable.md b/docs/framework/svelte/reference/type-aliases/SvelteTable.md index 0ae4dea06c..54798d7acd 100644 --- a/docs/framework/svelte/reference/type-aliases/SvelteTable.md +++ b/docs/framework/svelte/reference/type-aliases/SvelteTable.md @@ -3,47 +3,18 @@ id: SvelteTable title: SvelteTable --- -# Type Alias: SvelteTable\ +# Type Alias: SvelteTable\ ```ts -type SvelteTable = Omit, "store"> & object; +type SvelteTable = Table; ``` -Defined in: [packages/svelte-table/src/createTable.svelte.ts:14](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTable.svelte.ts#L14) +Defined in: [packages/svelte-table/src/createTable.svelte.ts:18](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTable.svelte.ts#L18) -## Type Declaration +A Svelte-aware TanStack Table instance. -### state - -```ts -readonly state: Readonly; -``` - -The selected state of the table. This state may not match the structure of -the full table state because it is selected by the selector function that -you pass as the 2nd argument to `createTable`. - -#### Example - -```ts -const table = createTable(options, (state) => ({ globalFilter: state.globalFilter })) // only globalFilter is part of the selected state - -console.log(table.state.globalFilter) -``` - -### ~~store~~ - -```ts -readonly store: Table["store"]; -``` - -#### Deprecated - -Prefer `table.state` for render reads, -`table.atoms..get()` for slice snapshots, or -`useSelector(table.store, selector)` for explicit subscriptions. -`table.store.state` is a current-value snapshot and is easy to misuse in -render code. +Table APIs and `table.atoms..get()` reads participate in Svelte +dependency tracking when used in templates, `$derived`, or `$effect`. ## Type Parameters @@ -54,7 +25,3 @@ render code. ### TData `TData` *extends* `RowData` - -### TSelected - -`TSelected` = `TableState`\<`TFeatures`\> diff --git a/examples/svelte/aggregation/src/App.svelte b/examples/svelte/aggregation/src/App.svelte index 17c646182c..5cccaefced 100644 --- a/examples/svelte/aggregation/src/App.svelte +++ b/examples/svelte/aggregation/src/App.svelte @@ -42,7 +42,8 @@ get data() { return data }, get meta() { return { rowSource } }, initialState: { pagination: { pageIndex: 0, pageSize: 10 } }, debugTable: true, debugColumns: true, - }, state => state) + }) + const pagination = $derived(table.atoms.pagination.get()) function changeRowSource(value: RowSource) { rowSource = value table.setOptions((previous) => ({ ...previous, meta: { rowSource: value } })) @@ -66,9 +67,9 @@
-
Page
{(table.state.pagination.pageIndex + 1).toLocaleString()} of {table.getPageCount().toLocaleString()}
- | Go to page: table.setPageIndex(event.currentTarget.value ? Number(event.currentTarget.value) - 1 : 0)} class="page-size-input" /> - +
Page
{(pagination.pageIndex + 1).toLocaleString()} of {table.getPageCount().toLocaleString()}
+ | Go to page: table.setPageIndex(event.currentTarget.value ? Number(event.currentTarget.value) - 1 : 0)} class="page-size-input" /> +
-
Showing {table.getRowModel().rows.length.toLocaleString()} of {table.getRowCount().toLocaleString()} Rows
{JSON.stringify(table.state, null, 2)}
+
Showing {table.getRowModel().rows.length.toLocaleString()} of {table.getRowCount().toLocaleString()} Rows
{JSON.stringify(table.store.get(), null, 2)}
diff --git a/examples/svelte/basic-app-table/src/App.svelte b/examples/svelte/basic-app-table/src/App.svelte index 6ae49d0c20..5ec2dd3afc 100644 --- a/examples/svelte/basic-app-table/src/App.svelte +++ b/examples/svelte/basic-app-table/src/App.svelte @@ -143,6 +143,6 @@
-
{JSON.stringify(table.state, null, 2)
+  
{JSON.stringify(table.store.get(), null, 2)
   }
diff --git a/examples/svelte/basic-dynamic-columns/src/App.svelte b/examples/svelte/basic-dynamic-columns/src/App.svelte index 10049e075f..d366a89aa7 100644 --- a/examples/svelte/basic-dynamic-columns/src/App.svelte +++ b/examples/svelte/basic-dynamic-columns/src/App.svelte @@ -53,7 +53,6 @@ }, debugTable: true, }, - (state) => state, ) diff --git a/examples/svelte/basic-external-atoms/src/App.svelte b/examples/svelte/basic-external-atoms/src/App.svelte index d527012f64..f3a9625396 100644 --- a/examples/svelte/basic-external-atoms/src/App.svelte +++ b/examples/svelte/basic-external-atoms/src/App.svelte @@ -204,5 +204,5 @@
-
{JSON.stringify(table.state, null, 2)}
+
{JSON.stringify(table.store.get(), null, 2)}
diff --git a/examples/svelte/basic-external-state/src/App.svelte b/examples/svelte/basic-external-state/src/App.svelte index 5e4652cf51..3397106800 100644 --- a/examples/svelte/basic-external-state/src/App.svelte +++ b/examples/svelte/basic-external-state/src/App.svelte @@ -202,5 +202,5 @@
-
{JSON.stringify(table.state, null, 2)}
+
{JSON.stringify(table.store.get(), null, 2)}
diff --git a/examples/svelte/cell-selection/src/App.svelte b/examples/svelte/cell-selection/src/App.svelte index 1435720cfa..945dacabbc 100644 --- a/examples/svelte/cell-selection/src/App.svelte +++ b/examples/svelte/cell-selection/src/App.svelte @@ -168,12 +168,6 @@ // isCellRangeSelectionEvent: event => Boolean(event.metaKey), // use Meta instead of Shift debugTable: true, }, - (state) => ({ - sorting: state.sorting, - columnVisibility: state.columnVisibility, - columnOrder: state.columnOrder, - columnPinning: state.columnPinning, - }), ) const randomizeColumns = () => @@ -189,9 +183,8 @@ // customize this to your needs let isFirstColumnLayout = true $effect(() => { - // read the atoms directly so this tracks only the layout slices. Reading - // table.state here would re-fire on every selection change, because the - // selector rebuilds that object each time any selected slice changes. + // Read only the layout atoms so selection changes do not re-run this + // effect. A full `table.store.get()` read would track every state slice. void table.atoms.columnOrder.get() void table.atoms.columnPinning.get() void table.atoms.columnVisibility.get() @@ -434,7 +427,7 @@
{data.length < 1_001
-        ? JSON.stringify(table.state, null, 2)
+        ? JSON.stringify(table.store.get(), null, 2)
         : ''}
diff --git a/examples/svelte/column-ordering/src/App.svelte b/examples/svelte/column-ordering/src/App.svelte index 1ba08361f8..8e9280f988 100644 --- a/examples/svelte/column-ordering/src/App.svelte +++ b/examples/svelte/column-ordering/src/App.svelte @@ -174,6 +174,6 @@ {/each} -
{JSON.stringify(table.state, null, 2)
+  
{JSON.stringify(table.store.get(), null, 2)
   }
diff --git a/examples/svelte/column-pinning-split/src/App.svelte b/examples/svelte/column-pinning-split/src/App.svelte index 885b3282e5..1c76e8089d 100644 --- a/examples/svelte/column-pinning-split/src/App.svelte +++ b/examples/svelte/column-pinning-split/src/App.svelte @@ -93,7 +93,6 @@ debugHeaders: true, debugColumns: true, }, - (state) => state, ) const randomizeColumns = () => { @@ -316,6 +315,6 @@ -
{JSON.stringify(table.state, null, 2)
+  
{JSON.stringify(table.store.get(), null, 2)
   }
diff --git a/examples/svelte/column-pinning-sticky/src/App.svelte b/examples/svelte/column-pinning-sticky/src/App.svelte index 272a799f5c..8f9d2333ea 100644 --- a/examples/svelte/column-pinning-sticky/src/App.svelte +++ b/examples/svelte/column-pinning-sticky/src/App.svelte @@ -125,7 +125,6 @@ debugHeaders: true, debugColumns: true, }, - (state) => state, ) const randomizeColumns = () => { @@ -252,6 +251,6 @@ -
{JSON.stringify(table.state, null, 2)
+  
{JSON.stringify(table.store.get(), null, 2)
   }
diff --git a/examples/svelte/column-pinning/src/App.svelte b/examples/svelte/column-pinning/src/App.svelte index 81f812674f..375d0c6609 100644 --- a/examples/svelte/column-pinning/src/App.svelte +++ b/examples/svelte/column-pinning/src/App.svelte @@ -303,5 +303,5 @@ }
-
{JSON.stringify(table.state, null, 2)}
+
{JSON.stringify(table.store.get(), null, 2)}
diff --git a/examples/svelte/column-resizing/src/App.svelte b/examples/svelte/column-resizing/src/App.svelte index ff4a6939c2..62c09feb99 100644 --- a/examples/svelte/column-resizing/src/App.svelte +++ b/examples/svelte/column-resizing/src/App.svelte @@ -96,14 +96,13 @@ debugHeaders: true, debugColumns: true, }, - (state) => state, ) function resizerTransform( header: ReturnType[number]['headers'][number], ) { if (columnResizeMode === 'onEnd' && header.column.getIsResizing()) { - const delta = table.state.columnResizing.deltaOffset ?? 0 + const delta = table.atoms.columnResizing.get().deltaOffset ?? 0 const dir = table.options.columnResizeDirection === 'rtl' ? -1 : 1 return `translateX(${dir * delta }px)` @@ -275,5 +274,5 @@
-
{JSON.stringify(table.state, null, 2)}
+
{JSON.stringify(table.store.get(), null, 2)}
diff --git a/examples/svelte/column-sizing/src/App.svelte b/examples/svelte/column-sizing/src/App.svelte index 712eb76b22..333016c021 100644 --- a/examples/svelte/column-sizing/src/App.svelte +++ b/examples/svelte/column-sizing/src/App.svelte @@ -67,7 +67,6 @@ debugHeaders: true, debugColumns: true, }, - (state) => state, ) @@ -88,7 +87,7 @@ value={column.getSize()} oninput={(e) => { table.setColumnSizing({ - ...table.state.columnSizing, + ...table.atoms.columnSizing.get(), [column.id]: Number((e.target as HTMLInputElement).value), }) }} @@ -204,5 +203,5 @@
-
{JSON.stringify(table.state, null, 2)}
+
{JSON.stringify(table.store.get(), null, 2)}
diff --git a/examples/svelte/column-visibility/src/App.svelte b/examples/svelte/column-visibility/src/App.svelte index ba12887e4b..2b809c3b51 100644 --- a/examples/svelte/column-visibility/src/App.svelte +++ b/examples/svelte/column-visibility/src/App.svelte @@ -172,6 +172,6 @@
-
{JSON.stringify(table.state, null, 2)
+  
{JSON.stringify(table.store.get(), null, 2)
   }
diff --git a/examples/svelte/column-visibility/tests/e2e/smoke.spec.ts b/examples/svelte/column-visibility/tests/e2e/smoke.spec.ts index 4741f78eaa..51b6bec6d4 100644 --- a/examples/svelte/column-visibility/tests/e2e/smoke.spec.ts +++ b/examples/svelte/column-visibility/tests/e2e/smoke.spec.ts @@ -94,7 +94,7 @@ function firstBodyRowCells(page: Page) { return page.locator('tbody tr').first().locator('td') } -/** Row data is random faker output, so `table.state` is the stable oracle. */ +/** Row data is random faker output, so the full state dump is the stable oracle. */ async function readColumnVisibility(page: Page) { const text = await page.getByTestId('table-state').textContent() const state = JSON.parse(text ?? '{}') as { diff --git a/examples/svelte/composable-tables/src/components/PaginationControls.svelte b/examples/svelte/composable-tables/src/components/PaginationControls.svelte index e56b58121f..f2cce0a518 100644 --- a/examples/svelte/composable-tables/src/components/PaginationControls.svelte +++ b/examples/svelte/composable-tables/src/components/PaginationControls.svelte @@ -7,6 +7,7 @@ } from '../hooks/table' const table = useTableContext() + const pagination = $derived(table.atoms.pagination.get())
{table.getRowModel().rows.length.toLocaleString()} Rows
-
{JSON.stringify(table.state, null, 2)}
+
{JSON.stringify(table.store.get(), null, 2)}
{#snippet Filter(column: Column, table: SvelteTable)} diff --git a/examples/svelte/filtering/src/App.svelte b/examples/svelte/filtering/src/App.svelte index 0a74ecd417..f436fe8646 100644 --- a/examples/svelte/filtering/src/App.svelte +++ b/examples/svelte/filtering/src/App.svelte @@ -81,8 +81,9 @@ debugHeaders: true, debugColumns: false, }, - (state) => state, ) + const pagination = $derived(table.atoms.pagination.get()) + const globalFilter = $derived(table.atoms.globalFilter.get())
@@ -91,7 +92,7 @@
table.setGlobalFilter(String(value))} class="summary-panel" placeholder="Search all columns..." @@ -163,7 +164,7 @@
Page
- {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {(pagination.pageIndex + 1).toLocaleString()} of{' '} {table.getPageCount().toLocaleString()}
@@ -173,7 +174,7 @@ type="number" min="1" max={table.getPageCount()} - value={table.state.pagination.pageIndex + 1} + value={pagination.pageIndex + 1} oninput={(e: Event) => { const page = (e.target as HTMLInputElement).value ? Number((e.target as HTMLInputElement).value) - 1 @@ -184,7 +185,7 @@ /> { table.setPageSize(Number((e.target as HTMLSelectElement).value)) }} @@ -152,5 +152,5 @@ Showing {table.getRowModel().rows.length.toLocaleString()} of{' '} {table.getPrePaginatedRowModel().rows.length.toLocaleString()} Rows -
{JSON.stringify(table.state, null, 2)}
+
{JSON.stringify(table.store.get(), null, 2)}
diff --git a/examples/svelte/filters-faceted/src/App.svelte b/examples/svelte/filters-faceted/src/App.svelte index 1c2034eb4f..e4ec288198 100644 --- a/examples/svelte/filters-faceted/src/App.svelte +++ b/examples/svelte/filters-faceted/src/App.svelte @@ -88,8 +88,9 @@ debugHeaders: true, debugColumns: false, }, - (state) => state, ) + const pagination = $derived(table.atoms.pagination.get()) + const globalFilter = $derived(table.atoms.globalFilter.get())
@@ -98,7 +99,7 @@
table.setGlobalFilter(String(value))} class="summary-panel" placeholder="Search all columns..." @@ -170,7 +171,7 @@
Page
- {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {(pagination.pageIndex + 1).toLocaleString()} of{' '} {table.getPageCount().toLocaleString()}
@@ -180,7 +181,7 @@ type="number" min="1" max={table.getPageCount()} - value={table.state.pagination.pageIndex + 1} + value={pagination.pageIndex + 1} oninput={(e: Event) => { const page = (e.target as HTMLInputElement).value ? Number((e.target as HTMLInputElement).value) - 1 @@ -191,7 +192,7 @@ /> { const page = e.currentTarget.value ? Number(e.currentTarget.value) - 1 @@ -200,7 +201,7 @@ />
{table.getPrePaginatedRowModel().rows.length.toLocaleString()} Rows
-
{JSON.stringify(table.state, null, 2)}
+
{JSON.stringify(table.store.get(), null, 2)}
diff --git a/examples/svelte/grouped-aggregation/src/App.svelte b/examples/svelte/grouped-aggregation/src/App.svelte index ff50a7af46..956fde753f 100644 --- a/examples/svelte/grouped-aggregation/src/App.svelte +++ b/examples/svelte/grouped-aggregation/src/App.svelte @@ -113,8 +113,8 @@ // manualGrouping: true, // pass rows that are already grouped and aggregated, for example from a server debugTable: true, }, - (state) => state, ) + const pagination = $derived(table.atoms.pagination.get())
@@ -241,7 +241,7 @@
Page
- {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {(pagination.pageIndex + 1).toLocaleString()} of{' '} {table.getPageCount().toLocaleString()}
@@ -251,7 +251,7 @@ type="number" min="1" max={table.getPageCount()} - value={table.state.pagination.pageIndex + 1} + value={pagination.pageIndex + 1} oninput={(e) => { const page = (e.target as HTMLInputElement).value ? Number((e.target as HTMLInputElement).value) - 1 @@ -262,7 +262,7 @@ />
{table.getRowModel().rows.length.toLocaleString()} Rows
-
{JSON.stringify(table.state, null, 2)}
+
{JSON.stringify(table.store.get(), null, 2)}
diff --git a/examples/svelte/grouping/src/App.svelte b/examples/svelte/grouping/src/App.svelte index 8b1ae023d6..2f9926611a 100644 --- a/examples/svelte/grouping/src/App.svelte +++ b/examples/svelte/grouping/src/App.svelte @@ -83,8 +83,8 @@ // manualGrouping: true, // pass rows that are already grouped, for example from a server debugTable: true, }, - (state) => state, ) + const pagination = $derived(table.atoms.pagination.get())
@@ -191,7 +191,7 @@
Page
- {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {(pagination.pageIndex + 1).toLocaleString()} of{' '} {table.getPageCount().toLocaleString()}
@@ -201,7 +201,7 @@ type="number" min="1" max={table.getPageCount()} - value={table.state.pagination.pageIndex + 1} + value={pagination.pageIndex + 1} oninput={(e) => { const page = (e.target as HTMLInputElement).value ? Number((e.target as HTMLInputElement).value) - 1 @@ -212,7 +212,7 @@ />
{table.getRowModel().rows.length.toLocaleString()} Rows
-
{JSON.stringify(table.state, null, 2)}
+
{JSON.stringify(table.store.get(), null, 2)}
diff --git a/examples/svelte/kitchen-sink/src/App.svelte b/examples/svelte/kitchen-sink/src/App.svelte index b6639deb4a..b51f4cdcb5 100644 --- a/examples/svelte/kitchen-sink/src/App.svelte +++ b/examples/svelte/kitchen-sink/src/App.svelte @@ -207,8 +207,10 @@ keepPinnedRows: true, debugTable: true, }, - (state) => state, ) + const pagination = $derived(table.atoms.pagination.get()) + const globalFilter = $derived(table.atoms.globalFilter.get()) + const grouping = $derived(table.atoms.grouping.get()) function debounceSet(key: string, setValue: () => void) { clearTimeout(debounceTimers.get(key)) @@ -253,7 +255,7 @@ } function cellClass(cell: Cell) { - const groupingActive = table.state.grouping.length > 0 + const groupingActive = grouping.length > 0 const hasAggregation = !!cell.column.columnDef.aggregationFn return !groupingActive ? undefined @@ -326,7 +328,7 @@ debounceSet('global', () => table.setGlobalFilter((event.target as HTMLInputElement).value), @@ -604,7 +606,7 @@
Page
- {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {(pagination.pageIndex + 1).toLocaleString()} of{' '} {table.getPageCount().toLocaleString()}
@@ -614,7 +616,7 @@ type="number" min="1" max={table.getPageCount()} - value={table.state.pagination.pageIndex + 1} + value={pagination.pageIndex + 1} oninput={(event) => { const page = (event.target as HTMLInputElement).value ? Number((event.target as HTMLInputElement).value) - 1 @@ -625,7 +627,7 @@ /> { table.setPageSize(Number((e.target as HTMLSelectElement).value)) }} @@ -184,5 +184,5 @@ {table.getRowCount().toLocaleString()} Rows
-
{JSON.stringify(table.state, null, 2)}
+
{JSON.stringify(table.store.get(), null, 2)}
diff --git a/examples/svelte/pagination/tests/e2e/smoke.spec.ts b/examples/svelte/pagination/tests/e2e/smoke.spec.ts index 6188138993..1ab3b8527e 100644 --- a/examples/svelte/pagination/tests/e2e/smoke.spec.ts +++ b/examples/svelte/pagination/tests/e2e/smoke.spec.ts @@ -69,6 +69,13 @@ async function getFirstBodyRowData(table: Locator) { return text?.replace(/\s+/g, ' ').trim() ?? '' } +async function readTableState(page: Page) { + const text = await page.getByTestId('table-state').textContent() + return JSON.parse(text ?? '{}') as { + pagination: { pageIndex: number; pageSize: number } + } +} + test('renders the table without crashing', async ({ page }) => { const { errors, server } = await openExample(page) @@ -102,3 +109,38 @@ test('renders the table without crashing', async ({ page }) => { await server.close() } }) + +test('keeps atom-backed pagination controls and the full state dump in sync', async ({ + page, +}) => { + const { errors, server } = await openExample(page) + + try { + const table = getTable(page) + const firstRowBefore = await getFirstBodyRowData(table) + const pageInput = page.locator('input.page-size-input') + const pageSize = page.locator('.controls select') + + await expect(pageInput).toHaveValue('1') + await page.getByRole('button', { name: '>', exact: true }).click() + + await expect(pageInput).toHaveValue('2') + await expect + .poll(() => readTableState(page)) + .toMatchObject({ + pagination: { pageIndex: 1, pageSize: 10 }, + }) + await expect.poll(() => getFirstBodyRowData(table)).not.toBe(firstRowBefore) + + await pageSize.selectOption('20') + + await expect + .poll(() => readTableState(page)) + .toMatchObject({ + pagination: { pageSize: 20 }, + }) + expect(errors).toEqual([]) + } finally { + await server.close() + } +}) diff --git a/examples/svelte/row-pinning/src/App.svelte b/examples/svelte/row-pinning/src/App.svelte index 74e026c29c..f63cf63dc1 100644 --- a/examples/svelte/row-pinning/src/App.svelte +++ b/examples/svelte/row-pinning/src/App.svelte @@ -107,12 +107,8 @@ debugTable: true, debugAll: true, }, - (state) => ({ - pagination: state.pagination, - rowPinning: state.rowPinning, - expanded: state.expanded, - }), ) + const pagination = $derived(table.atoms.pagination.get()) {#snippet PinCell(row: ReturnType['rows'][0])} @@ -184,7 +180,7 @@ {/snippet} -{#snippet Filter(column: Column, tableRef: SvelteTable)} +{#snippet Filter(column: Column, tableRef: SvelteTable)} {@const firstValue = tableRef .getPreFilteredRowModel() .flatRows[0]?.getValue(column.id)} @@ -313,7 +309,7 @@
Page
- {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {(pagination.pageIndex + 1).toLocaleString()} of{' '} {table.getPageCount().toLocaleString()}
@@ -323,7 +319,7 @@ type="number" min="1" max={table.getPageCount()} - value={table.state.pagination.pageIndex + 1} + value={pagination.pageIndex + 1} oninput={(e) => { const page = (e.target as HTMLInputElement).value ? Number((e.target as HTMLInputElement).value) - 1 @@ -334,7 +330,7 @@ /> table.setGlobalFilter((e.target as HTMLInputElement).value)} class="summary-panel" placeholder="Search all columns..." @@ -248,7 +246,7 @@
Page
- {(table.state.pagination.pageIndex + 1).toLocaleString()} of {table.getPageCount().toLocaleString()} + {(pagination.pageIndex + 1).toLocaleString()} of {table.getPageCount().toLocaleString()}
@@ -257,7 +255,7 @@ type="number" min="1" max={table.getPageCount()} - value={table.state.pagination.pageIndex + 1} + value={pagination.pageIndex + 1} oninput={(e) => { const page = (e.target as HTMLInputElement).value ? Number((e.target as HTMLInputElement).value) - 1 : 0 table.setPageIndex(page) @@ -266,7 +264,7 @@ /> { table.setPageSize(Number(event.currentTarget.value)) }} diff --git a/examples/svelte/with-tanstack-query/src/App.svelte b/examples/svelte/with-tanstack-query/src/App.svelte index 2a313452c0..c9dea96e21 100644 --- a/examples/svelte/with-tanstack-query/src/App.svelte +++ b/examples/svelte/with-tanstack-query/src/App.svelte @@ -77,7 +77,6 @@ manualPagination: true, debugTable: true, }, - (state) => state, ) @@ -182,5 +181,5 @@ Showing {table.getRowModel().rows.length.toLocaleString()} of{' '} {dataQuery.data?.rowCount?.toLocaleString() ?? 0} Rows -
{JSON.stringify(table.state, null, 2)}
+
{JSON.stringify(table.store.get(), null, 2)}
diff --git a/examples/vue/pagination/src/App.vue b/examples/vue/pagination/src/App.vue index e7bddd5de8..28cfc3c403 100644 --- a/examples/vue/pagination/src/App.vue +++ b/examples/vue/pagination/src/App.vue @@ -18,8 +18,6 @@ const features = tableFeatures({ const columnHelper = createColumnHelper() -const INITIAL_PAGE_INDEX = 0 - const pageSizes = [10, 20, 30, 40, 50] const data = ref(makeData(1_000)) diff --git a/knip.json b/knip.json index 69199651ff..c1de8bdf83 100644 --- a/knip.json +++ b/knip.json @@ -40,7 +40,8 @@ "entry": [ "src/index.ts!", "src/static-functions.ts!", - "src/flex-render.ts!" + "src/flex-render.ts!", + "tests/public-api.types.ts!" ] } } diff --git a/packages/svelte-table/skills/migrate-v8-to-v9/SKILL.md b/packages/svelte-table/skills/migrate-v8-to-v9/SKILL.md index e7182bb58a..a3c8815f25 100644 --- a/packages/svelte-table/skills/migrate-v8-to-v9/SKILL.md +++ b/packages/svelte-table/skills/migrate-v8-to-v9/SKILL.md @@ -50,7 +50,7 @@ const table = createTable({ | v8 | v9 | | -------------------------------------------- | ---------------------------------------------------------- | -| `createSvelteTable(options)` | `createTable(options, selector?)` | +| `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` | @@ -78,9 +78,10 @@ Factories take no arguments. Register `filterFns`, `sortFns`, and `aggregationFn ## 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 `table.state.sorting`, `table.store.state.sorting`, or the narrow `table.atoms.sorting.get()`. -- `table.state` contains all registered state by default. Pass a second-argument selector to `createTable` only to narrow its reactive surface. -- `subscribeTable(table.atoms.pagination, selector?)` exposes `.current` for fine-grained template subscriptions. +- `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. +- Starting in beta.59, remove second-argument selectors from `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. - 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`. diff --git a/packages/svelte-table/skills/table-state/SKILL.md b/packages/svelte-table/skills/table-state/SKILL.md index dbd4da5217..524a152b0f 100644 --- a/packages/svelte-table/skills/table-state/SKILL.md +++ b/packages/svelte-table/skills/table-state/SKILL.md @@ -1,7 +1,7 @@ --- name: table-state description: > - Use Svelte 5 rune-backed table.atoms/store and selected table.state, reactive option getters, controlled $state slices, value-or-updater callbacks, external atoms, and auto-reset behavior without snapshot mismatches. + Use Svelte 5 rune-aware table atoms and stores, reactive option getters, controlled $state slices, value-or-updater callbacks, external atoms, and auto-reset behavior without snapshot mismatches. metadata: type: framework library: '@tanstack/svelte-table' @@ -26,13 +26,14 @@ TanStack Table is primarily a state coordinator. Keep state internal unless anot - `table.baseAtoms` are internal writable atoms initialized from resolved initial state. - `table.atoms` are readonly derived atoms for the active owner of each registered slice. - `table.store` is the readonly flat store assembled from those atoms. -- `table.state` is only the result selected by the second `createTable` argument. -Svelte 5 backs these surfaces with runes and synchronizes reactive options before DOM updates. Only registered features create state and types. If pagination is missing, register `rowPaginationFeature`; do not add a cast or an ad hoc state field. Keep `features` and `columns` stable and pass changing `data` through a getter. +The Svelte adapter bridges TanStack Store dependency tracking into Svelte runes. `table.atoms..get()`, `table.store.get()`, and table APIs become reactive when called in a template, `$derived`, `$derived.by`, or `$effect`. Outside those contexts they return current snapshots. + +Only registered features create state and types. If pagination is missing, register `rowPaginationFeature`; do not add a cast or ad hoc state field. Keep `features` and `columns` stable and pass changing `data` through a getter. ## Setup -Keep state internal unless another subsystem needs to own it. Select only render state that the component needs. +Keep state internal unless another subsystem needs to own it. Read only the state slices a component needs. ```svelte ``` ## Core Patterns +### Read narrow or complete state + +```ts +const pagination = $derived(table.atoms.pagination.get()) +const pageIndex = $derived(table.atoms.pagination.get().pageIndex) +const rows = $derived(table.getRowModel().rows) +const stateJson = $derived(JSON.stringify(table.store.get(), null, 2)) +``` + +Use atom reads for normal UI. A `table.store.get()` read intentionally re-runs for any registered state change, so reserve it for debug output, persistence, or computations that need the whole state. + ### Control a slice with value-or-updater semantics ```ts @@ -75,31 +87,29 @@ const updatePagination = (next: Updater) => { } ``` -Pass `get state() { return { pagination } }` and `onPaginationChange: updatePagination` to `createTable`. +Pass a getter-backed `state.pagination` and `onPaginationChange: updatePagination` to `createTable`. -### Subscribe narrowly outside selected table.state +`createTableState` is a convenience for the same updater-compatible pattern: ```ts -import { subscribeTable } from '@tanstack/svelte-table' - -const pageIndex = subscribeTable( - table.atoms.pagination, - (value) => value.pageIndex, -) +const [pagination, setPagination] = createTableState({ + pageIndex: 0, + pageSize: 20, +}) ``` -Read `pageIndex.current` in rune-tracked Svelte code. Use feature APIs for writes; `baseAtoms` is a low-level escape hatch. - ## Choose State Ownership Use one owner per slice: - Prefer internal state plus feature APIs for table-local interaction. - Use `initialState` for starting/reset values; changing it later does not reset state. -- Prefer a stable external atom in `atoms` for state shared with Query, routing, or another component. Do not also add its change callback. -- Use a `$state` value exposed through a `state` getter plus the matching callback for simple controlled state. Always resolve value-or-updater semantics. +- Use Svelte `$state`, a getter-backed `state` entry, and the matching callback for normal Svelte-owned controlled state. +- Use a stable external atom in `atoms` for state shared as a raw TanStack Store atom. Do not also add its change callback. + +External atoms win over controlled `state`, which syncs into the internal base atom. Avoid multiple owners. The global v8 `onStateChange` option is gone; subscribe to `table.store` if every state change must be observed imperatively. -External atoms win over controlled `state`, which syncs into the internal base atom. Avoid multiple owners. The global v8 `onStateChange` option is gone; subscribe to `table.store` if all state changes must be observed. +When code outside the table consumes a raw external atom, use `useSelector` from `@tanstack/svelte-store`. Inside table-driven UI, read the rune-aware `table.atoms..get()` wrapper. ## Initialize, Update, and Reset @@ -127,8 +137,10 @@ Correct: ```ts const options = { - get state() { - return { pagination } + state: { + get pagination() { + return pagination + }, }, onPaginationChange: updatePagination, } @@ -138,24 +150,21 @@ A controlled slice is frozen unless every updater is resolved into the owning ru Source: `docs/framework/svelte/guide/table-state.md` -### HIGH Reading snapshots outside tracking +### HIGH Snapshotting outside tracking Wrong: ```ts -const pageIndex = table.store.state.pagination.pageIndex +const pageIndex = table.store.get().pagination.pageIndex ``` -Correct: +Correct inside a component: ```ts -const pageIndex = subscribeTable( - table.atoms.pagination, - (value) => value.pageIndex, -) +const pageIndex = $derived(table.atoms.pagination.get().pageIndex) ``` -`store.state` is a current snapshot; it does not create a future Svelte update outside a tracked scope. +The first line is only a current snapshot when it runs outside a template or rune. The second line is a narrow native Svelte derivation. Source: `packages/svelte-table/src/createTable.svelte.ts` @@ -171,8 +180,10 @@ Correct: ```ts const options = { - get state() { - return { pagination } + state: { + get pagination() { + return pagination + }, }, } ``` @@ -202,4 +213,4 @@ Source: `docs/framework/svelte/guide/pagination.md` ## API Discovery -Inspect `node_modules/@tanstack/svelte-table/dist/createTable.svelte.d.ts`, `createTableState.svelte.d.ts`, and `subscribe.d.ts`; inspect registered state slices in the matching core feature source. +Inspect `node_modules/@tanstack/svelte-table/dist/createTable.svelte.d.ts`, `createTableHook.svelte.d.ts`, and `createTableState.svelte.d.ts`; inspect registered state slices in the matching core feature source. diff --git a/packages/svelte-table/src/createTable.svelte.ts b/packages/svelte-table/src/createTable.svelte.ts index 068352daaa..a0dcd5c483 100644 --- a/packages/svelte-table/src/createTable.svelte.ts +++ b/packages/svelte-table/src/createTable.svelte.ts @@ -1,5 +1,4 @@ import { constructTable } from '@tanstack/table-core' -import { useSelector } from '@tanstack/svelte-store' import { untrack } from 'svelte' import { flatMerge, mergeObjects } from './merge-objects' import { svelteReactivity } from './reactivity.svelte' @@ -8,67 +7,46 @@ import type { Table, TableFeatures, TableOptions, - TableState, } from '@tanstack/table-core' +/** + * A Svelte-aware TanStack Table instance. + * + * Table APIs and `table.atoms..get()` reads participate in Svelte + * dependency tracking when used in templates, `$derived`, or `$effect`. + */ export type SvelteTable< TFeatures extends TableFeatures, TData extends RowData, - TSelected = TableState, -> = Omit, 'store'> & { - /** - * @deprecated Prefer `table.state` for render reads, - * `table.atoms..get()` for slice snapshots, or - * `useSelector(table.store, selector)` for explicit subscriptions. - * `table.store.state` is a current-value snapshot and is easy to misuse in - * render code. - */ - readonly store: Table['store'] - /** - * The selected state of the table. This state may not match the structure of - * the full table state because it is selected by the selector function that - * you pass as the 2nd argument to `createTable`. - * - * @example - * const table = createTable(options, (state) => ({ globalFilter: state.globalFilter })) // only globalFilter is part of the selected state - * - * console.log(table.state.globalFilter) - */ - readonly state: Readonly -} +> = Table /** * Creates a Svelte 5 table instance backed by rune-aware TanStack Store atoms. * - * The optional selector projects from `table.store`; the selected value is - * exposed on `table.state`. The adapter syncs options in `$effect.pre`, so - * reactive option getters and external `$state` values are applied before DOM - * updates read table APIs such as `getRowModel()`. + * Read a specific state slice with `table.atoms..get()` and read the + * complete state with `table.store.get()`. Those reads participate in Svelte + * dependency tracking when they run in a template, `$derived`, or `$effect`. + * The adapter syncs options in `$effect.pre`, so reactive option getters and + * external `$state` values are applied before DOM updates read table APIs such + * as `getRowModel()`. * * @example * ```svelte * * - * {table.state.pagination.pageIndex} + * Page {pagination.pageIndex + 1} + *
{stateJson}
* ``` */ export function createTable< TFeatures extends TableFeatures, TData extends RowData, - TSelected = TableState, ->( - tableOptions: TableOptions, - selector?: (state: TableState) => TSelected, -): SvelteTable { +>(tableOptions: TableOptions): SvelteTable { // 1. Merge reactivity into options using mergeObjects (preserves getters) const mergedOptions = mergeObjects(tableOptions, { features: { @@ -91,11 +69,7 @@ export function createTable< ) as TableOptions // 3. Construct table - const table = constructTable(resolvedOptions) as unknown as SvelteTable< - TFeatures, - TData, - TSelected - > + const table = constructTable(resolvedOptions) // 4. Sync options reactively. When controlled state changes (e.g., $state // inside createTableState), the effect re-runs and calls setOptions. @@ -122,16 +96,5 @@ export function createTable< }) }) - // 5. State selector - const stateStore = useSelector(table.store, selector) - - Object.defineProperty(table, 'state', { - get() { - return stateStore.current - }, - configurable: true, - enumerable: true, - }) - return table } diff --git a/packages/svelte-table/src/createTableHook.svelte.ts b/packages/svelte-table/src/createTableHook.svelte.ts index 36e9417be4..5b0170fb73 100644 --- a/packages/svelte-table/src/createTableHook.svelte.ts +++ b/packages/svelte-table/src/createTableHook.svelte.ts @@ -34,7 +34,6 @@ import type { Table, TableFeatures, TableOptions, - TableState, } from '@tanstack/table-core' export type ComponentType> = Component @@ -285,16 +284,16 @@ export type CreateTableHookOptions< // ============================================================================= /** - * Extended table API returned by createAppTable with all App wrapper components. + * Svelte-aware table returned by `createAppTable`, extended with the registered + * table components and the `App*` context wrappers. */ export type AppSvelteTable< TFeatures extends TableFeatures, TData extends RowData, - TSelected, TTableComponents extends Record>, TCellComponents extends Record>, THeaderComponents extends Record>, -> = SvelteTable & +> = SvelteTable & NoInfer & { /** * Root wrapper component that provides table context. @@ -397,14 +396,16 @@ export interface CreateTableHookResult< /** * Creates a table with the `App*` wrapper components and registered * `tableComponents` attached. `TData` is inferred from the `data` option. + * + * Read table state with `table.atoms..get()` or `table.store.get()`. + * These reads participate in Svelte dependency tracking inside templates, + * `$derived`, and `$effect`. */ - createAppTable: >( + createAppTable: ( tableOptions: Omit, 'features'>, - selector?: (state: TableState) => TSelected, ) => AppSvelteTable< TFeatures, TData, - TSelected, TTableComponents, TCellComponents, THeaderComponents @@ -413,20 +414,10 @@ export interface CreateTableHookResult< * Reads the table provided by the nearest ``. This is the same * extended instance `createAppTable` returns, so the `App*` components and your * `tableComponents` are available on it. - * - * Pass `TSelected` to match the selector you gave `createAppTable`, so - * `table.state` is typed as the selected slice. It cannot be inferred - * automatically (context does not carry the provider's generics), so it - * defaults to the full table state, which is correct for the common case of - * `createAppTable` without a selector. */ - useTableContext: < - TData extends RowData = RowData, - TSelected = TableState, - >() => AppSvelteTable< + useTableContext: () => AppSvelteTable< TFeatures, TData, - TSelected, TTableComponents, TCellComponents, THeaderComponents @@ -538,13 +529,9 @@ export function createTableHook< * Use this in custom `tableComponents` passed to `createTableHook`. * TFeatures is already known from the createTableHook call. */ - function useTableContext< - TData extends RowData = RowData, - TSelected = TableState, - >(): AppSvelteTable< + function useTableContext(): AppSvelteTable< TFeatures, TData, - TSelected, TTableComponents, TCellComponents, THeaderComponents @@ -564,7 +551,6 @@ export function createTableHook< return table as unknown as AppSvelteTable< TFeatures, TData, - TSelected, TTableComponents, TCellComponents, THeaderComponents @@ -632,16 +618,11 @@ export function createTableHook< * * TFeatures is already known from the createTableHook call; TData is inferred from the data prop. */ - function createAppTable< - TData extends RowData, - TSelected = TableState, - >( + function createAppTable( tableOptions: Omit, 'features'>, - selector?: (state: TableState) => TSelected, ): AppSvelteTable< TFeatures, TData, - TSelected, TTableComponents, TCellComponents, THeaderComponents @@ -652,10 +633,7 @@ export function createTableHook< tableOptions, ) as TableOptions - const table = createTable( - mergedTableOptions, - selector, - ) + const table = createTable(mergedTableOptions) // Build cellComponents with FlexRender included const cellComponentsWithFlexRender = { @@ -727,7 +705,6 @@ export function createTableHook< }) as AppSvelteTable< TFeatures, TData, - TSelected, TTableComponents, TCellComponents, THeaderComponents diff --git a/packages/svelte-table/src/index.ts b/packages/svelte-table/src/index.ts index 75c8ab8fbf..9ea3f448e9 100644 --- a/packages/svelte-table/src/index.ts +++ b/packages/svelte-table/src/index.ts @@ -18,5 +18,4 @@ export type { } from './createTableHook.svelte' export { createTableState } from './createTableState.svelte' export { default as FlexRender } from './FlexRender.svelte' -export { subscribeTable, type SubscribeSource } from './subscribe' export { renderComponent, renderSnippet } from './render-component' diff --git a/packages/svelte-table/src/reactivity.svelte.ts b/packages/svelte-table/src/reactivity.svelte.ts index bfe5bef784..a251a7452e 100644 --- a/packages/svelte-table/src/reactivity.svelte.ts +++ b/packages/svelte-table/src/reactivity.svelte.ts @@ -53,7 +53,9 @@ function createRuneWritableAtom(initialValue: T): Atom { * * Table state atoms are backed by TanStack Store atoms. The options store stays * framework-native because row-model APIs read `table.options` directly during - * render. Readonly table atoms bridge Store dependency tracking into `$derived.by`. + * render. Readonly table atoms bridge Store dependency tracking into + * `$derived.by`, so their `.get()` methods participate in Svelte dependency + * tracking when called in templates, `$derived`, or `$effect`. */ export function svelteReactivity(): TableReactivityBindings { return { @@ -91,6 +93,9 @@ export function svelteReactivity(): TableReactivityBindings { return { get: () => { + // Both reads are load-bearing: the Store read preserves dependency + // tracking between table atoms, while touching `value` registers the + // current Svelte reactive scope with the rune-backed bridge. const currentValue = storeAtom.get() value return currentValue diff --git a/packages/svelte-table/src/subscribe.ts b/packages/svelte-table/src/subscribe.ts deleted file mode 100644 index 5af55d8ff4..0000000000 --- a/packages/svelte-table/src/subscribe.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { shallow, useSelector } from '@tanstack/svelte-store' -import type { - Atom, - ReadonlyAtom, - ReadonlyStore, - Store, -} from '@tanstack/svelte-store' - -export type SubscribeSource = - | Atom - | ReadonlyAtom - | Store - | ReadonlyStore - -/** - * Creates a fine-grained Svelte subscription to a TanStack Store source. - * - * Pass a table atom or store and optionally project it with a selector. The - * returned selector store exposes `.current`, making it useful for reading - * focused table state outside the broad `createTable` selector. - * - * @example - * ```svelte - * - * - * - * ``` - */ -export function subscribeTable( - source: SubscribeSource, -): ReturnType> -export function subscribeTable( - source: SubscribeSource, - selector: (state: TSourceValue) => TSelected, -): ReturnType> -export function subscribeTable( - source: SubscribeSource, - selector?: (state: TSourceValue) => TSelected, -) { - return useSelector(source, selector, { compare: shallow }) -} diff --git a/packages/svelte-table/tests/adapter-lifecycle.test.ts b/packages/svelte-table/tests/adapter-lifecycle.test.ts index fce850d689..ac6d03bae5 100644 --- a/packages/svelte-table/tests/adapter-lifecycle.test.ts +++ b/packages/svelte-table/tests/adapter-lifecycle.test.ts @@ -6,46 +6,42 @@ import { createAtom } from '@tanstack/svelte-store' import CallbackHarness from './fixtures/CallbackHarness.svelte' import PaginationHarness from './fixtures/PaginationHarness.svelte' import ReactivityHarness from './fixtures/ReactivityHarness.svelte' -import SelectorHarness from './fixtures/SelectorHarness.svelte' -import type { OnChangeFn, RowSelectionState } from '@tanstack/table-core' +import RuneStateHarness from './fixtures/RuneStateHarness.svelte' +import type { + OnChangeFn, + RowSelectionState, + TableState, + stockFeatures, +} from '@tanstack/table-core' function outputText(name: string) { return screen.getByRole('status', { name }).textContent } describe('Svelte adapter lifecycle and reactive options', () => { - test('unmount unsubscribes external atoms and stops later reactions', async () => { + test('unmount stops later external atom reactions', async () => { const externalRowSelection = createAtom({ 2: true }) - const subscribeSpy = vi.spyOn(externalRowSelection, 'subscribe') const selectionCaptor = vi.fn<(selection: RowSelectionState) => void>() - const selectionStoreCaptor = - vi.fn<(store: { readonly current: RowSelectionState }) => void>() const { unmount } = render(ReactivityHarness, { externalRowSelection, selectionCaptor, - selectionStoreCaptor, }) await act() expect(outputText('Selected rows')).toBe('{"2":true}') expect(selectionCaptor.mock.calls).toEqual([[{ 2: true }]]) - expect(subscribeSpy).toHaveBeenCalled() await act(() => externalRowSelection.set({ 1: true })) expect(outputText('Selected rows')).toBe('{"1":true}') expect(selectionCaptor.mock.calls).toEqual([[{ 2: true }], [{ 1: true }]]) - expect(selectionStoreCaptor).toHaveBeenCalledOnce() unmount() await act(() => externalRowSelection.set({ 2: true })) expect(selectionCaptor.mock.calls).toEqual([[{ 2: true }], [{ 1: true }]]) - expect(selectionStoreCaptor.mock.lastCall?.[0].current).toEqual({ - 1: true, - }) }) test('external atoms take precedence over controlled state and receive table updates', async () => { @@ -148,24 +144,32 @@ describe('Svelte adapter lifecycle and reactive options', () => { expect(outputText('Paginated row ids')).toBe('0,1,2') }) - test('selector subscriptions only rerun for their selected dependency', async () => { + test('native rune projections only rerun for their tracked dependency', async () => { const selectedRowCaptor = vi.fn<(selected: boolean) => void>() - const wholeSelectionCaptor = vi.fn<(selection: RowSelectionState) => void>() + const selectionCaptor = vi.fn<(selection: RowSelectionState) => void>() + const stateCaptor = + vi.fn<(state: TableState) => void>() - render(SelectorHarness, { selectedRowCaptor, wholeSelectionCaptor }) + render(RuneStateHarness, { + selectedRowCaptor, + selectionCaptor, + stateCaptor, + }) await act() expect(outputText('Selected first row')).toBe('false') expect(outputText('Whole row selection')).toBe('{}') expect(selectedRowCaptor.mock.calls).toEqual([[false]]) - expect(wholeSelectionCaptor.mock.calls).toEqual([[{}]]) + expect(selectionCaptor.mock.calls).toEqual([[{}]]) + expect(stateCaptor).toHaveBeenCalledTimes(1) await fireEvent.click( screen.getByRole('button', { name: 'Select first row' }), ) expect(selectedRowCaptor.mock.calls).toEqual([[false], [true]]) - expect(wholeSelectionCaptor.mock.calls).toEqual([[{}], [{ 1: true }]]) + expect(selectionCaptor.mock.calls).toEqual([[{}], [{ 1: true }]]) + expect(stateCaptor).toHaveBeenCalledTimes(2) await fireEvent.click( screen.getByRole('button', { name: 'Select second row too' }), @@ -174,20 +178,28 @@ describe('Svelte adapter lifecycle and reactive options', () => { expect(outputText('Selected first row')).toBe('true') expect(outputText('Whole row selection')).toBe('{"1":true,"2":true}') expect(selectedRowCaptor).toHaveBeenCalledTimes(2) - expect(wholeSelectionCaptor.mock.calls).toEqual([ + expect(selectionCaptor.mock.calls).toEqual([ [{}], [{ 1: true }], [{ 1: true, 2: true }], ]) + expect(stateCaptor).toHaveBeenCalledTimes(3) await fireEvent.click( screen.getByRole('button', { name: 'Set unrelated page size' }), ) + + expect(selectedRowCaptor).toHaveBeenCalledTimes(2) + expect(selectionCaptor).toHaveBeenCalledTimes(3) + expect(stateCaptor).toHaveBeenCalledTimes(4) + expect(stateCaptor.mock.lastCall?.[0].pagination.pageSize).toBe(20) + await fireEvent.click( - screen.getByRole('button', { name: 'Select second row too' }), + screen.getByRole('button', { name: 'Publish same selection' }), ) expect(selectedRowCaptor).toHaveBeenCalledTimes(2) - expect(wholeSelectionCaptor).toHaveBeenCalledTimes(3) + expect(selectionCaptor).toHaveBeenCalledTimes(3) + expect(stateCaptor).toHaveBeenCalledTimes(4) }) }) diff --git a/packages/svelte-table/tests/fixtures/HookHarness.svelte b/packages/svelte-table/tests/fixtures/HookHarness.svelte index 896cac7204..39e75c49b7 100644 --- a/packages/svelte-table/tests/fixtures/HookHarness.svelte +++ b/packages/svelte-table/tests/fixtures/HookHarness.svelte @@ -5,7 +5,7 @@ import type { HookData } from './hook-fixture' interface Props { - tableCaptor?: (table: AppSvelteTable) => void + tableCaptor?: (table: AppSvelteTable) => void } let { tableCaptor }: Props = $props() diff --git a/packages/svelte-table/tests/fixtures/ReactivityHarness.svelte b/packages/svelte-table/tests/fixtures/ReactivityHarness.svelte index 112565dc16..1bfc7834e4 100644 --- a/packages/svelte-table/tests/fixtures/ReactivityHarness.svelte +++ b/packages/svelte-table/tests/fixtures/ReactivityHarness.svelte @@ -2,7 +2,6 @@ import { untrack } from 'svelte' import { createPaginatedRowModel, stockFeatures } from '@tanstack/table-core' import { createTable } from '../../src/createTable.svelte' - import { subscribeTable } from '../../src/subscribe' import type { Atom } from '@tanstack/svelte-store' import type { ColumnDef, RowSelectionState } from '@tanstack/table-core' @@ -18,18 +17,11 @@ interface Props { externalRowSelection?: Atom selectionCaptor?: (selection: RowSelectionState) => void - selectionStoreCaptor?: (store: { - readonly current: RowSelectionState - }) => void snapshotCaptor?: (snapshot: Snapshot) => void } - let { - externalRowSelection, - selectionCaptor, - selectionStoreCaptor, - snapshotCaptor, - }: Props = $props() + let { externalRowSelection, selectionCaptor, snapshotCaptor }: Props = + $props() const externalSelectionAtom = untrack(() => externalRowSelection) const features = { @@ -73,15 +65,10 @@ ? { rowSelection: externalSelectionAtom } : undefined, }) - const selected = subscribeTable(table.atoms.rowSelection) - const lifecycleSelection = externalSelectionAtom - ? subscribeTable(externalSelectionAtom) - : selected - - untrack(() => selectionStoreCaptor?.(lifecycleSelection)) + const selection = $derived(table.atoms.rowSelection.get()) $effect(() => { - selectionCaptor?.(lifecycleSelection.current) + selectionCaptor?.(selection) }) $effect(() => { @@ -107,7 +94,7 @@ } -{JSON.stringify(selected.current)} +{JSON.stringify(selection)} {table .getRowModel() diff --git a/packages/svelte-table/tests/fixtures/RuneStateHarness.svelte b/packages/svelte-table/tests/fixtures/RuneStateHarness.svelte new file mode 100644 index 0000000000..d9c633c55d --- /dev/null +++ b/packages/svelte-table/tests/fixtures/RuneStateHarness.svelte @@ -0,0 +1,51 @@ + + +{String(selectedRow)} +{JSON.stringify(selection)} +{JSON.stringify(state)} + + + + + diff --git a/packages/svelte-table/tests/fixtures/SelectorHarness.svelte b/packages/svelte-table/tests/fixtures/SelectorHarness.svelte deleted file mode 100644 index 2eb5be2435..0000000000 --- a/packages/svelte-table/tests/fixtures/SelectorHarness.svelte +++ /dev/null @@ -1,47 +0,0 @@ - - -{String(selectedRow.current)} -{JSON.stringify(wholeSelection.current)} - - - - diff --git a/packages/svelte-table/tests/fixtures/SsrHarness.svelte b/packages/svelte-table/tests/fixtures/SsrHarness.svelte index 994da785b3..dd97ee9b2b 100644 --- a/packages/svelte-table/tests/fixtures/SsrHarness.svelte +++ b/packages/svelte-table/tests/fixtures/SsrHarness.svelte @@ -46,7 +46,7 @@ {JSON.stringify(table.state.rowSelection)}{JSON.stringify(table.atoms.rowSelection.get())} = table +const appTable = hook.createAppTable({ + data: options.data, + columns: options.columns, +}) +const typedAppTable: AppSvelteTable = + appTable +const appOptions = { data: options.data, columns: options.columns } +const removedSelector = (state: unknown) => state + +// @ts-expect-error The Svelte adapter no longer accepts a creation selector. +createTable(options, (state) => state) + +// @ts-expect-error createAppTable accepts only its table options. +hook.createAppTable(appOptions, removedSelector) + +// @ts-expect-error Selected table.state was removed in v9.0.0-beta.59. +table.state + +// @ts-expect-error SvelteTable no longer has a selected-state generic. +type RemovedSelectedState = SvelteTable + +type ExpectFalse = T +type HasSubscribeTable = 'subscribeTable' extends keyof typeof publicApi + ? true + : false +type SubscribeTableWasRemoved = ExpectFalse + +type RemovedSubscribeSource = SubscribeSource + +void svelteTable +void typedAppTable +export type PublicApiAssertions = + | SubscribeTableWasRemoved + | RemovedSelectedState + | RemovedSubscribeSource diff --git a/packages/svelte-table/tests/rendering.test.ts b/packages/svelte-table/tests/rendering.test.ts index 033fdbc68e..fadb933e9d 100644 --- a/packages/svelte-table/tests/rendering.test.ts +++ b/packages/svelte-table/tests/rendering.test.ts @@ -180,12 +180,13 @@ describe('FlexRender', () => { describe('createTableHook', () => { test('binds defaults, wrapper components, contexts, and render helpers', () => { const tableCaptor = - vi.fn<(table: AppSvelteTable) => void>() + vi.fn<(table: AppSvelteTable) => void>() render(HookHarness, { tableCaptor }) const table = tableCaptor.mock.lastCall?.[0] expect(hook.appFeatures).toBe(stockFeatures) + expect(table).not.toHaveProperty('state') expect(table?.getRowModel().rows[0]?.id).toBe('row-1') expect(table?.TableBadge).toEqual(expect.any(Function)) expect(table?.AppTable).toEqual(expect.any(Function)) diff --git a/perf-done.md b/perf-done.md index 9729c45602..163b371cb0 100644 --- a/perf-done.md +++ b/perf-done.md @@ -7,14 +7,15 @@ Entries are sorted by adjusted effectiveness score descending. ## Counts -- **Entries:** 58 -- **Source findings:** 56 +- **Entries:** 59 +- **Source findings:** 57 - **Cross-cutting sweeps:** 2 - 2026-07-03: #102 (C9) moved here from perf-todo.md after the row-model benchmark confirmed and the fix landed. - 2026-07-07: #68 (A4) moved here from perf-todo.md after implementation. - 2026-07-07: #76 (A5) moved here from perf-todo.md after implementation. - 2026-07-07: #85 (A6) moved here from perf-todo.md after implementation. - 2026-07-07: #92 (C11) moved here from perf-todo.md after implementation. +- 2026-07-29: #65 (F1) moved here from perf-todo.md after the Svelte selector layer was removed. ## Score 9 @@ -682,6 +683,41 @@ function compareAlphanumeric(aStr: string, bStr: string) { --- +## 65. F1: Svelte useSelector subscribes with `===` compare: every subscriber re-renders on every atom write — Score: 8 + +**Status:** `[x]` done +**Implementation note:** Completed 2026-07-29 for v9.0.0-beta.59 with a stronger resolution than the proposed shallow comparison. The Svelte creation selector, selector-backed `table.state`, `subscribeTable`, and `SubscribeSource` were removed. `createTable` and `createAppTable` now create no adapter-wide selector subscription. Consumers read rune-aware `table.atoms..get()` values for narrow dependencies, use `table.store.get()` when they intentionally need the complete state, and use native `$derived` projections for equality-gated derived values. Unit coverage verifies that unrelated pagination writes do not rerun a row-selection-derived effect while full-store consumers still update; package tests, every Svelte example type/build project, and every Svelte example e2e project pass. + +**Location:** Formerly `packages/svelte-table/src/createTable.svelte.ts:125`; the selector path and `packages/svelte-table/src/subscribe.ts` are removed. +**Category:** `memoization` + +Hot path before removal: Every state atom write (resize tick = 2 writes at 60-120Hz, selection, sorting, everything) → store snapshot recompute → `useSelector` subscription callback. `@tanstack/svelte-store`'s `useSelector` defaults `compare` to `===` (verified: `defaultCompare(a,b){return a===b}` and `options.compare ?? defaultCompare`). The store snapshot is a fresh object on every recompute, and the idiomatic selector (`(state) => ({ pagination: state.pagination })`) returns a fresh object too, so `compare(slice, data)` is always false, `slice = data` writes the `$state` rune on EVERY table state write, and every template reading `table.state` re-renders per tick even when the selected slice is unchanged. Verified: even the default no-selector path re-assigned the `$state` slice on every write (identity selector over a fresh snapshot). The former sibling `subscribe.ts:45` passed `{ compare: shallow }`, as do React/Preact `useTable`. + +**Before** + +```ts +// Adapter-wide selector subscription +const stateStore = useSelector(table.store, selector) +Object.defineProperty(table, 'state', { + get: () => stateStore.current, +}) +``` + +**After** + +```ts +const table = createTable(options) +const pagination = $derived(table.atoms.pagination.get()) +const fullState = $derived(table.store.get()) +``` + +**Big-O:** The adapter-wide O(writes × selector subscribers) invalidation path is removed. Narrow consumers now invalidate only when their atom or `$derived` projection changes; a full-store consumer remains intentionally O(all state writes). At 100Hz resize, a pagination consumer no longer receives the selector layer's spurious invalidations. + +**Risk:** Intentional beta breaking change: creation selectors, `table.state`, `subscribeTable`, `SubscribeSource`, and selected-state generic parameters are gone with no compatibility layer. Svelte templates and rune scopes provide the reactive boundary directly. +**Verification:** CONFIRMED and implemented. No adapter-wide selector subscription remains; focused lifecycle tests verify narrow versus full-store invalidation behavior, including identical selection writes. + +--- + ## 67. A2: grouping/groupedColumnMode omitted from sizing/pinning/visibility memoDeps: header/cell column misalignment after setGrouping (bug) — Score: 8 (bug) **Status:** `[x]` done diff --git a/perf-todo.md b/perf-todo.md index 9d096497fd..2210c621aa 100644 --- a/perf-todo.md +++ b/perf-todo.md @@ -7,49 +7,18 @@ Entries are sorted by adjusted effectiveness score descending. ## Counts -- **Entries:** 75 -- **Source findings:** 75 +- **Entries:** 74 +- **Source findings:** 74 - **Cross-cutting sweeps:** 0 - 2026-07-03: #102 (C9) completed and moved to perf-done.md. - 2026-07-07: #68 (A4) completed and moved to perf-done.md. - 2026-07-07: #76 (A5) completed and moved to perf-done.md. - 2026-07-07: #85 (A6) completed and moved to perf-done.md. - 2026-07-07: #92 (C11) completed and moved to perf-done.md. +- 2026-07-29: #65 (F1) completed and moved to perf-done.md after the Svelte selector layer was removed. ## Score 8 -## 65. F1: Svelte useSelector subscribes with `===` compare: every subscriber re-renders on every atom write — Score: 8 - -**Status:** `[ ]` not started -**Implementation note:** _(none)_ - -**Location:** `packages/svelte-table/src/createTable.svelte.ts:125` -**Category:** `memoization` - -Hot path: Every state atom write (resize tick = 2 writes at 60-120Hz, selection, sorting, everything) → store snapshot recompute → `useSelector` subscription callback. `@tanstack/svelte-store`'s `useSelector` defaults `compare` to `===` (verified: `defaultCompare(a,b){return a===b}` and `options.compare ?? defaultCompare`). The store snapshot is a fresh object on every recompute, and the idiomatic selector (`(state) => ({ pagination: state.pagination })`) returns a fresh object too, so `compare(slice, data)` is always false, `slice = data` writes the `$state` rune on EVERY table state write, and every template reading `table.state` re-renders per tick even when the selected slice is unchanged. Verified: even the default no-selector path re-assigns the `$state` slice on every write (identity selector over a fresh snapshot). The sibling `subscribe.ts:45` correctly passes `{ compare: shallow }`, as do React/Preact `useTable`. - -**Before** - -```ts -// 5. State selector -const stateStore = useSelector(table.store, selector) -``` - -**After** - -```ts -import { shallow, useSelector } from '@tanstack/svelte-store' -// ... -const stateStore = useSelector(table.store, selector, { compare: shallow }) -``` - -**Big-O:** O(writes × subscribers) re-renders → O(changed-slice writes × subscribers). At 100Hz resize with a `pagination` selector: 200 spurious `$state` invalidations/sec eliminated per consumer, each of which cascades to a Svelte template re-evaluation over the visible grid. Shallow compare additionally avoids re-proxying the slice per tick. - -**Risk:** Matches react/preact/subscribe.ts behavior exactly; only risk is a consumer relying on re-render-per-write with an identity selector (they should use `subscribe`/atoms for that). -**Verification:** CONFIRMED (drop-in, adapter-wide, per-write). - ---- - ## Score 7 ## 70. B19: constructRow allocates Object.values(table.\_features) and probes all features per row — Score: 7 From 0a310dcc2e614d0c0fba0eb2ed1f91e4374e9ac3 Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Wed, 29 Jul 2026 20:54:10 -0500 Subject: [PATCH 2/3] update svelte-table skills --- _artifacts/domain_map.yaml | 14 ++++-- _artifacts/skill_spec.md | 13 +++-- _artifacts/skill_tree.yaml | 15 +++--- .../skills/migrate-v8-to-v9/SKILL.md | 7 ++- .../svelte-table/skills/table-state/SKILL.md | 50 ++++++++++++++++++- .../skills/with-tanstack-query/SKILL.md | 6 ++- 6 files changed, 83 insertions(+), 22 deletions(-) diff --git a/_artifacts/domain_map.yaml b/_artifacts/domain_map.yaml index 70359e636e..d68d818d69 100644 --- a/_artifacts/domain_map.yaml +++ b/_artifacts/domain_map.yaml @@ -11,7 +11,7 @@ library: meta: generated_by: '@tanstack/intent scaffold domain discovery' - date: '2026-07-10' + date: '2026-07-29' status: reviewed maintainer_review_pending: false phase_4_date: '2026-07-10' @@ -892,7 +892,7 @@ skills: domain: framework-adapters type: framework framework: svelte - purpose: 'Create a Svelte 5 table with createTable, rune-backed options, snippets, and headless markup.' + purpose: 'Create a Svelte 5 table with createTable, rune-backed option getters, FlexRender, and headless markup.' sources: [ 'TanStack/table:docs/framework/svelte/guide/migrating.md', @@ -909,16 +909,19 @@ skills: domain: framework-adapters type: framework framework: svelte - purpose: 'Use rune-backed atoms, selected table.state, controlled slices, and external atoms without reactivity mismatches.' + purpose: 'Use rune-aware table atoms and stores, native $derived projections, controlled $state or createTableState slices, and external atoms without broad invalidation or snapshot mismatches.' sources: [ 'TanStack/table:docs/framework/svelte/guide/table-state.md', 'TanStack/table:docs/framework/svelte/guide/pagination.md', 'TanStack/table:examples/svelte/basic-external-state', 'TanStack/table:packages/svelte-table/src/createTable.svelte.ts', + 'TanStack/table:packages/svelte-table/src/createTableState.svelte.ts', ] failure_modes: - - 'Reading or passing non-reactive snapshots where createTable options need getters over $state or $derived values.' + - 'Keeping the removed beta.58 createTable/createAppTable selector argument, selected table.state property, subscribeTable helper, or SubscribeSource type after beta.59.' + - 'Reading atom or store snapshots outside a tracked Svelte scope and expecting the read itself to keep a consumer reactive.' + - 'Reading table.store.get() in an effect that only needs one atom slice, causing unrelated state changes to rerun the effect.' - 'Pairing onSliceChange with a controlled state slice that is not actually written back with value-or-updater semantics.' - 'Blaming pagination reactivity when autoResetPageIndex immediately overwrites an externally requested page.' @@ -937,6 +940,7 @@ skills: failure_modes: - 'Attempting the v9 adapter migration without first adopting Svelte 5-compatible component and reactivity syntax.' - 'Keeping readable-store assumptions or v8 row-model table options after switching to createTable.' + - 'Keeping beta.58 creation selectors, table.state, subscribeTable, or their removed selected-state generic parameters after beta.59.' - 'Missing shared v9 prototype, feature, helper, sorting, and logical-pinning changes.' - slug: create-table-hook @@ -952,7 +956,7 @@ skills: 'TanStack/table:packages/svelte-table/src/createTableHook.svelte.ts', ] failure_modes: - - 'Defining the hook in a plain module that cannot carry the required Svelte rune semantics.' + - 'Reimplementing createAppTable around the framework-agnostic core instead of using the shipped rune-capable createTableHook implementation.' - 'Passing $state snapshots instead of getters to createAppTable, freezing data or controlled state.' - 'Prop-drilling contexts through registered components instead of consuming the typed hooks under matching App wrappers.' diff --git a/_artifacts/skill_spec.md b/_artifacts/skill_spec.md index 6f45dd3c24..5868132346 100644 --- a/_artifacts/skill_spec.md +++ b/_artifacts/skill_spec.md @@ -1,8 +1,8 @@ # TanStack Table v9 skill specification -Status: reviewed -Date: 2026-07-10 -Library target: TanStack Table v9, authored in stable-release voice +Status: reviewed
+Date: 2026-07-29
+Library target: TanStack Table v9, authored in stable-release voice
Package metadata target: exact workspace package versions; release automation keeps every shipped skill synchronized This specification is the generation contract for a deliberately smaller, foot-gun-first TanStack Intent skill set. It is not a documentation outline. The complete evidence and failure-mode inventory is in domain_map.yaml. @@ -305,8 +305,11 @@ All Devtools skills must emphasize the required non-empty table options.key, lif ### Svelte - V9 targets Svelte 5 and runes. +- Read a slice with `table.atoms..get()` and the complete state with `table.store.get()` inside templates, `$derived`, `$derived.by`, or `$effect`; reads outside tracked scopes are current snapshots. +- Starting in beta.59, Svelte has no table-creation selector, selected `table.state`, `subscribeTable`, or `SubscribeSource`; use native `$derived` projections instead. +- Prefer `$state` plus getter-backed controlled slices, or `createTableState` for an updater-compatible getter/setter pair. Keep `useSelector` only for raw external atoms consumed outside the table. - Reactive data and controlled values commonly need getters; avoid passing snapshots. -- createTableHook implementation belongs in a rune-capable module. +- The shipped `createTableHook` implementation supplies rune semantics, so an app hook that calls it may live in a normal `.ts` module. ### Vue @@ -342,7 +345,7 @@ All Devtools skills must emphasize the required non-empty table options.key, lif ## Cross-cutting placement rules -- Performance: stable inputs in getting-started/core; selectors in table-state; CSS variables in resizing; measurement/overscan in Virtual; row ownership in client-vs-server. +- Performance: stable inputs in getting-started/core; adapter-specific fine-grained state reads or selectors in table-state; CSS variables in resizing; measurement/overscan in Virtual; row ownership in client-vs-server. - CSS: pinning, sizing, resizing, and Virtual skills only. Core may state that CSS is user-owned. - Accessibility: core/getting-started may remind that headless rendering leaves semantics and interaction accessibility with the renderer; do not create a component-library integration skill. - Query: data source and manual processing boundaries, not Table rendering. diff --git a/_artifacts/skill_tree.yaml b/_artifacts/skill_tree.yaml index 9f50dbf2f8..5d06c586fb 100644 --- a/_artifacts/skill_tree.yaml +++ b/_artifacts/skill_tree.yaml @@ -8,9 +8,9 @@ library: generated_from: domain_map: '_artifacts/domain_map.yaml' skill_spec: '_artifacts/skill_spec.md' -generated_at: '2026-07-10' +generated_at: '2026-07-29' status: reviewed -reviewed_at: '2026-07-10' +reviewed_at: '2026-07-29' batch_review: true structure: 'flat-per-package' monorepo_layout: true @@ -607,7 +607,7 @@ skills: domain: framework-adapters path: 'packages/svelte-table/skills/getting-started/SKILL.md' package: 'packages/svelte-table' - description: 'Create a Svelte 5 TanStack Table v9 table with createTable, explicit tableFeatures, rune-backed data getters, stable static inputs, snippets, and headless markup. Load when replacing createSvelteTable or pre-rune patterns.' + description: 'Create a Svelte 5 TanStack Table v9 table with createTable, explicit tableFeatures, rune-backed data getters, stable static inputs, FlexRender, and headless markup. Load when replacing createSvelteTable or pre-rune patterns.' requires: ['@tanstack/table-core#core', '@tanstack/table-core#table-features'] sources: @@ -621,13 +621,14 @@ skills: domain: framework-adapters path: 'packages/svelte-table/skills/table-state/SKILL.md' package: 'packages/svelte-table' - description: 'Use Svelte 5 rune-backed table.atoms/store and selected table.state, reactive option getters, controlled $state slices, value-or-updater callbacks, external atoms, and auto-reset behavior without snapshot mismatches.' + description: 'Use Svelte 5 rune-aware table atoms and stores, $derived projections, reactive option getters, controlled $state or createTableState slices, external atoms, and auto-reset behavior without broad invalidation or snapshot mismatches.' requires: ['@tanstack/table-core#core', 'getting-started'] sources: - 'TanStack/table:docs/framework/svelte/guide/table-state.md' + - 'TanStack/table:docs/framework/svelte/guide/pagination.md' - 'TanStack/table:examples/svelte/basic-external-state' - 'TanStack/table:packages/svelte-table/src/createTable.svelte.ts' - - 'TanStack/table:docs/framework/svelte/guide/pagination.md' + - 'TanStack/table:packages/svelte-table/src/createTableState.svelte.ts' - name: 'Svelte Migrate v8 to v9' slug: migrate-v8-to-v9 @@ -635,7 +636,7 @@ skills: domain: framework-adapters path: 'packages/svelte-table/skills/migrate-v8-to-v9/SKILL.md' package: 'packages/svelte-table' - description: 'Migrate Svelte v8/store-oriented tables to the Svelte 5 v9 createTable adapter, runes, explicit feature and row-model slots, current rendering, prototype methods, sortFn names, helpers, and logical pinning.' + description: '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.' requires: [ '@tanstack/table-core#migrate-v8-to-v9', @@ -653,7 +654,7 @@ skills: domain: framework-adapters path: 'packages/svelte-table/skills/create-table-hook/SKILL.md' package: 'packages/svelte-table' - description: 'Define a Svelte createAppTable/createAppColumnHelper in a rune-capable module with shared features/defaults, reactive per-table getters, optional App component registries, and typed table/cell/header context hooks.' + description: "Define a Svelte createAppTable/createAppColumnHelper using the adapter's rune-capable createTableHook implementation, shared features/defaults, reactive per-table getters, optional App component registries, and typed table/cell/header context hooks." requires: ['@tanstack/table-core#core', 'getting-started', 'table-state'] sources: - 'TanStack/table:docs/framework/svelte/guide/composable-tables.md' diff --git a/packages/svelte-table/skills/migrate-v8-to-v9/SKILL.md b/packages/svelte-table/skills/migrate-v8-to-v9/SKILL.md index a3c8815f25..c46a956687 100644 --- a/packages/svelte-table/skills/migrate-v8-to-v9/SKILL.md +++ b/packages/svelte-table/skills/migrate-v8-to-v9/SKILL.md @@ -1,7 +1,7 @@ --- name: migrate-v8-to-v9 description: > - Complete Svelte v8-to-v9 migration reference: Svelte 5, createTable, explicit features and row-model slots, atom/rune state, rendering helpers, prototype methods, type generics, sorting, sizing, selection, and logical pinning. + 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. metadata: type: lifecycle library: '@tanstack/svelte-table' @@ -174,6 +174,10 @@ Register both the feature and its `create*RowModel()` slot. Leaving `get*RowMode Use `get data() { return data }`; a one-time `data` snapshot does not remain reactive. +### HIGH: Keeping beta.58 Svelte selectors + +Remove second arguments from `createTable` and `createAppTable`, replace selected `table.state` reads with `table.atoms..get()` or `table.store.get()`, and remove `subscribeTable`, `SubscribeSource`, and selected-state generic parameters. Beta.59 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. @@ -185,6 +189,7 @@ Keep calls bound to row/cell/column/header instances; shallow copies do not cont - [ ] 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. diff --git a/packages/svelte-table/skills/table-state/SKILL.md b/packages/svelte-table/skills/table-state/SKILL.md index 524a152b0f..e289e580ed 100644 --- a/packages/svelte-table/skills/table-state/SKILL.md +++ b/packages/svelte-table/skills/table-state/SKILL.md @@ -1,7 +1,7 @@ --- name: table-state description: > - Use Svelte 5 rune-aware table atoms and stores, reactive option getters, controlled $state slices, value-or-updater callbacks, external atoms, and auto-reset behavior without snapshot mismatches. + Use Svelte 5 rune-aware table atoms and stores, $derived projections, reactive option getters, controlled $state or createTableState slices, external atoms, and auto-reset behavior without broad invalidation or snapshot mismatches. metadata: type: framework library: '@tanstack/svelte-table' @@ -15,6 +15,7 @@ sources: - 'TanStack/table:docs/framework/svelte/guide/pagination.md' - 'TanStack/table:examples/svelte/basic-external-state' - 'TanStack/table:packages/svelte-table/src/createTable.svelte.ts' + - 'TanStack/table:packages/svelte-table/src/createTableState.svelte.ts' --- This skill builds on `@tanstack/table-core#core` and `getting-started`. Read them first for table ownership and Svelte construction. @@ -89,15 +90,40 @@ const updatePagination = (next: Updater) => { Pass a getter-backed `state.pagination` and `onPaginationChange: updatePagination` to `createTable`. -`createTableState` is a convenience for the same updater-compatible pattern: +### Reduce boilerplate with `createTableState` ```ts +import { + createTable, + createTableState, + rowPaginationFeature, + tableFeatures, + type PaginationState, +} from '@tanstack/svelte-table' + +const features = tableFeatures({ rowPaginationFeature }) +const columns = [{ accessorKey: 'name' }] +const data = [{ name: 'Ada' }] const [pagination, setPagination] = createTableState({ pageIndex: 0, pageSize: 20, }) + +const table = createTable({ + features, + columns, + data, + state: { + get pagination() { + return pagination() + }, + }, + onPaginationChange: setPagination, +}) ``` +For better or worse, this resembles a small React `useState` hook: `pagination()` reads the current rune-backed value, while `setPagination` accepts either a value or a functional updater and can be passed directly to `onPaginationChange`. + ## Choose State Ownership Use one owner per slice: @@ -125,6 +151,26 @@ Feature resets use `table.initialState` unless `true` requests the feature defau ## Common Mistakes +### HIGH Keeping removed adapter selectors + +Wrong: + +```ts +const table = createTable(options, (state) => state.pagination) +const pageIndex = table.state.pageIndex +``` + +Correct: + +```ts +const table = createTable(options) +const pagination = $derived(table.atoms.pagination.get()) +``` + +Starting in beta.59, `createTable` and `createAppTable` take only options, `table.state` is absent, and `subscribeTable` and `SubscribeSource` are no longer exported. Use native tracked Svelte reads and `$derived` projections. + +Source: `docs/framework/svelte/guide/migrating.md` + ### HIGH Controlling without writing back Wrong: diff --git a/packages/svelte-table/skills/with-tanstack-query/SKILL.md b/packages/svelte-table/skills/with-tanstack-query/SKILL.md index 2933ce3fe4..3f84b71451 100644 --- a/packages/svelte-table/skills/with-tanstack-query/SKILL.md +++ b/packages/svelte-table/skills/with-tanstack-query/SKILL.md @@ -52,8 +52,10 @@ const table = createTable({ return dataQuery.data?.rowCount ?? 0 }, manualPagination: true, - get state() { - return { pagination } + state: { + get pagination() { + return pagination + }, }, onPaginationChange: (next) => { pagination = typeof next === 'function' ? next(pagination) : next From d36f37ed48c33ebdfb7171fa1f6875a3e5ed9790 Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Wed, 29 Jul 2026 21:24:48 -0500 Subject: [PATCH 3/3] address legit pr feedback --- docs/framework/svelte/guide/column-filtering.md | 4 +++- docs/framework/svelte/guide/migrating.md | 2 +- docs/framework/svelte/guide/row-selection.md | 2 +- docs/framework/svelte/guide/sorting.md | 4 +++- .../svelte/reference/functions/createTable.md | 10 ++++++++-- packages/svelte-table/src/createTable.svelte.ts | 8 +++++++- 6 files changed, 23 insertions(+), 7 deletions(-) diff --git a/docs/framework/svelte/guide/column-filtering.md b/docs/framework/svelte/guide/column-filtering.md index fdd303d8f6..1972fb7c90 100644 --- a/docs/framework/svelte/guide/column-filtering.md +++ b/docs/framework/svelte/guide/column-filtering.md @@ -150,7 +150,9 @@ const table = createTable({ const columnFilters = $derived(table.atoms.columnFilters.get()) -table.atoms.columnFilters.get() // current snapshot in an event handler +const logCurrentColumnFilters = () => { + console.log(table.atoms.columnFilters.get()) +} ``` However, if you need access to the column filter state outside of the table, you can "control" the column filter state like down below. diff --git a/docs/framework/svelte/guide/migrating.md b/docs/framework/svelte/guide/migrating.md index de52651f3e..c2cd59e6ab 100644 --- a/docs/framework/svelte/guide/migrating.md +++ b/docs/framework/svelte/guide/migrating.md @@ -3,7 +3,7 @@ title: Migrating to TanStack Table V9 (Svelte) --- > [!NOTE] -> `v9.0.0-beta.59` removes Svelte's table-creation selectors. `createTable` and `createAppTable` now accept only their options object; the selected `table.state` property, `subscribeTable`, and `SubscribeSource` are also removed. Read one slice with `table.atoms..get()` and the complete state with `table.store.get()`. These reads update naturally in Svelte templates, `$derived`, `$derived.by`, and `$effect`. This is an intentional beta breaking change with no compatibility overload or runtime shim. See [State Management Changes](#state-management-changes) for examples. +> `v9.0.0-beta.59` removes Svelte's table-creation selectors. `createTable` and `createAppTable` now accept only their options object; the selected `table.state` property, `subscribeTable`, and `SubscribeSource` are also removed. Read one slice with `table.atoms..get()` and the complete state with `table.store.get()`. These reads update naturally in Svelte templates, `$derived`, `$derived.by`, and `$effect`. This is an intentional beta-breaking change with no compatibility overload or runtime shim. See [State Management Changes](#state-management-changes) for examples. > [!NOTE] > `v9.0.0-beta.48`/`beta.49` split aggregation out of `columnGroupingFeature` into a new `rowAggregationFeature` (`stockFeatures` includes both). If you declare features explicitly, add `rowAggregationFeature` anywhere you use `aggregationFns`, `aggregationFn`, `aggregatedCell`, `cell.getIsAggregated()`, or `column.getAggregationValue()`. Aggregation function definitions, row-depth selection, and the `getAggregationValue` signature also changed. See [Grouping and Aggregation](#grouping-and-aggregation) below. diff --git a/docs/framework/svelte/guide/row-selection.md b/docs/framework/svelte/guide/row-selection.md index f14efc8800..ba364267e4 100644 --- a/docs/framework/svelte/guide/row-selection.md +++ b/docs/framework/svelte/guide/row-selection.md @@ -46,7 +46,7 @@ The table instance already manages the row selection state for you. You can acce - `getGroupedSelectedRowModel()` - returns selected rows after grouping and sorting ```ts -console.log(table.atoms.rowSelection.get()) //get the row selection state - { 1: true, 2: false, etc... } +console.log(table.atoms.rowSelection.get()) // get the row selection state - { 1: true, 2: true } console.log(table.getSelectedRowModel().rows) //get full client-side selected rows console.log(table.getFilteredSelectedRowModel().rows) //get filtered client-side selected rows console.log(table.getGroupedSelectedRowModel().rows) //get grouped client-side selected rows diff --git a/docs/framework/svelte/guide/sorting.md b/docs/framework/svelte/guide/sorting.md index 75fc1a54d1..c38e18b26a 100644 --- a/docs/framework/svelte/guide/sorting.md +++ b/docs/framework/svelte/guide/sorting.md @@ -78,7 +78,9 @@ const table = createTable({ const sorting = $derived(table.atoms.sorting.get()) -table.atoms.sorting.get() // current snapshot in an event handler +const logCurrentSorting = () => { + console.log(table.atoms.sorting.get()) +} ``` However, if you need access to the sorting state outside of the table, you can "control" the sorting state like down below. diff --git a/docs/framework/svelte/reference/functions/createTable.md b/docs/framework/svelte/reference/functions/createTable.md index b5fb364aaf..77306b22eb 100644 --- a/docs/framework/svelte/reference/functions/createTable.md +++ b/docs/framework/svelte/reference/functions/createTable.md @@ -9,7 +9,7 @@ title: createTable function createTable(tableOptions): SvelteTable; ``` -Defined in: [packages/svelte-table/src/createTable.svelte.ts:46](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTable.svelte.ts#L46) +Defined in: [packages/svelte-table/src/createTable.svelte.ts:52](https://github.com/TanStack/table/blob/main/packages/svelte-table/src/createTable.svelte.ts#L52) Creates a Svelte 5 table instance backed by rune-aware TanStack Store atoms. @@ -44,7 +44,13 @@ as `getRowModel()`. ```svelte