Skip to content

Commit 8b60e08

Browse files
authored
feat: remove svelte-table selectors to be inline with other signal-ba… (#6464)
* feat: remove svelte-table selectors to be inline with other signal-based adapters * update svelte-table skills * address legit pr feedback
1 parent 3b2eb64 commit 8b60e08

89 files changed

Lines changed: 909 additions & 972 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎_artifacts/domain_map.yaml‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ library:
1111

1212
meta:
1313
generated_by: '@tanstack/intent scaffold domain discovery'
14-
date: '2026-07-10'
14+
date: '2026-07-29'
1515
status: reviewed
1616
maintainer_review_pending: false
1717
phase_4_date: '2026-07-10'
@@ -892,7 +892,7 @@ skills:
892892
domain: framework-adapters
893893
type: framework
894894
framework: svelte
895-
purpose: 'Create a Svelte 5 table with createTable, rune-backed options, snippets, and headless markup.'
895+
purpose: 'Create a Svelte 5 table with createTable, rune-backed option getters, FlexRender, and headless markup.'
896896
sources:
897897
[
898898
'TanStack/table:docs/framework/svelte/guide/migrating.md',
@@ -909,16 +909,19 @@ skills:
909909
domain: framework-adapters
910910
type: framework
911911
framework: svelte
912-
purpose: 'Use rune-backed atoms, selected table.state, controlled slices, and external atoms without reactivity mismatches.'
912+
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.'
913913
sources:
914914
[
915915
'TanStack/table:docs/framework/svelte/guide/table-state.md',
916916
'TanStack/table:docs/framework/svelte/guide/pagination.md',
917917
'TanStack/table:examples/svelte/basic-external-state',
918918
'TanStack/table:packages/svelte-table/src/createTable.svelte.ts',
919+
'TanStack/table:packages/svelte-table/src/createTableState.svelte.ts',
919920
]
920921
failure_modes:
921-
- 'Reading or passing non-reactive snapshots where createTable options need getters over $state or $derived values.'
922+
- 'Keeping the removed beta.58 createTable/createAppTable selector argument, selected table.state property, subscribeTable helper, or SubscribeSource type after beta.59.'
923+
- 'Reading atom or store snapshots outside a tracked Svelte scope and expecting the read itself to keep a consumer reactive.'
924+
- 'Reading table.store.get() in an effect that only needs one atom slice, causing unrelated state changes to rerun the effect.'
922925
- 'Pairing onSliceChange with a controlled state slice that is not actually written back with value-or-updater semantics.'
923926
- 'Blaming pagination reactivity when autoResetPageIndex immediately overwrites an externally requested page.'
924927

@@ -937,6 +940,7 @@ skills:
937940
failure_modes:
938941
- 'Attempting the v9 adapter migration without first adopting Svelte 5-compatible component and reactivity syntax.'
939942
- 'Keeping readable-store assumptions or v8 row-model table options after switching to createTable.'
943+
- 'Keeping beta.58 creation selectors, table.state, subscribeTable, or their removed selected-state generic parameters after beta.59.'
940944
- 'Missing shared v9 prototype, feature, helper, sorting, and logical-pinning changes.'
941945

942946
- slug: create-table-hook
@@ -952,7 +956,7 @@ skills:
952956
'TanStack/table:packages/svelte-table/src/createTableHook.svelte.ts',
953957
]
954958
failure_modes:
955-
- 'Defining the hook in a plain module that cannot carry the required Svelte rune semantics.'
959+
- 'Reimplementing createAppTable around the framework-agnostic core instead of using the shipped rune-capable createTableHook implementation.'
956960
- 'Passing $state snapshots instead of getters to createAppTable, freezing data or controlled state.'
957961
- 'Prop-drilling contexts through registered components instead of consuming the typed hooks under matching App wrappers.'
958962

‎_artifacts/skill_spec.md‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# TanStack Table v9 skill specification
22

3-
Status: reviewed
4-
Date: 2026-07-10
5-
Library target: TanStack Table v9, authored in stable-release voice
3+
Status: reviewed<br>
4+
Date: 2026-07-29<br>
5+
Library target: TanStack Table v9, authored in stable-release voice<br>
66
Package metadata target: exact workspace package versions; release automation keeps every shipped skill synchronized
77

88
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
305305
### Svelte
306306

307307
- V9 targets Svelte 5 and runes.
308+
- Read a slice with `table.atoms.<slice>.get()` and the complete state with `table.store.get()` inside templates, `$derived`, `$derived.by`, or `$effect`; reads outside tracked scopes are current snapshots.
309+
- Starting in beta.59, Svelte has no table-creation selector, selected `table.state`, `subscribeTable`, or `SubscribeSource`; use native `$derived` projections instead.
310+
- 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.
308311
- Reactive data and controlled values commonly need getters; avoid passing snapshots.
309-
- createTableHook implementation belongs in a rune-capable module.
312+
- The shipped `createTableHook` implementation supplies rune semantics, so an app hook that calls it may live in a normal `.ts` module.
310313

311314
### Vue
312315

@@ -342,7 +345,7 @@ All Devtools skills must emphasize the required non-empty table options.key, lif
342345

343346
## Cross-cutting placement rules
344347

345-
- 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.
348+
- 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.
346349
- CSS: pinning, sizing, resizing, and Virtual skills only. Core may state that CSS is user-owned.
347350
- 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.
348351
- Query: data source and manual processing boundaries, not Table rendering.

‎_artifacts/skill_tree.yaml‎

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ library:
88
generated_from:
99
domain_map: '_artifacts/domain_map.yaml'
1010
skill_spec: '_artifacts/skill_spec.md'
11-
generated_at: '2026-07-10'
11+
generated_at: '2026-07-29'
1212
status: reviewed
13-
reviewed_at: '2026-07-10'
13+
reviewed_at: '2026-07-29'
1414
batch_review: true
1515
structure: 'flat-per-package'
1616
monorepo_layout: true
@@ -607,7 +607,7 @@ skills:
607607
domain: framework-adapters
608608
path: 'packages/svelte-table/skills/getting-started/SKILL.md'
609609
package: 'packages/svelte-table'
610-
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.'
610+
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.'
611611
requires:
612612
['@tanstack/table-core#core', '@tanstack/table-core#table-features']
613613
sources:
@@ -621,21 +621,22 @@ skills:
621621
domain: framework-adapters
622622
path: 'packages/svelte-table/skills/table-state/SKILL.md'
623623
package: 'packages/svelte-table'
624-
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.'
624+
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.'
625625
requires: ['@tanstack/table-core#core', 'getting-started']
626626
sources:
627627
- 'TanStack/table:docs/framework/svelte/guide/table-state.md'
628+
- 'TanStack/table:docs/framework/svelte/guide/pagination.md'
628629
- 'TanStack/table:examples/svelte/basic-external-state'
629630
- 'TanStack/table:packages/svelte-table/src/createTable.svelte.ts'
630-
- 'TanStack/table:docs/framework/svelte/guide/pagination.md'
631+
- 'TanStack/table:packages/svelte-table/src/createTableState.svelte.ts'
631632

632633
- name: 'Svelte Migrate v8 to v9'
633634
slug: migrate-v8-to-v9
634635
type: lifecycle
635636
domain: framework-adapters
636637
path: 'packages/svelte-table/skills/migrate-v8-to-v9/SKILL.md'
637638
package: 'packages/svelte-table'
638-
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.'
639+
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.'
639640
requires:
640641
[
641642
'@tanstack/table-core#migrate-v8-to-v9',
@@ -653,7 +654,7 @@ skills:
653654
domain: framework-adapters
654655
path: 'packages/svelte-table/skills/create-table-hook/SKILL.md'
655656
package: 'packages/svelte-table'
656-
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.'
657+
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."
657658
requires: ['@tanstack/table-core#core', 'getting-started', 'table-state']
658659
sources:
659660
- 'TanStack/table:docs/framework/svelte/guide/composable-tables.md'

‎docs/config.json‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,6 @@
590590
{ "label": "createTableState", "to": "framework/svelte/reference/functions/createTableState" },
591591
{ "label": "SvelteTable", "to": "framework/svelte/reference/type-aliases/SvelteTable" },
592592
{ "label": "AppSvelteTable", "to": "framework/svelte/reference/type-aliases/AppSvelteTable" },
593-
{ "label": "subscribeTable", "to": "framework/svelte/reference/functions/subscribeTable" },
594593
{ "label": "renderComponent", "to": "framework/svelte/reference/functions/renderComponent" },
595594
{ "label": "renderSnippet", "to": "framework/svelte/reference/functions/renderSnippet" },
596595
{ "label": "FlexRender", "to": "framework/svelte/reference/variables/FlexRender" }

‎docs/framework/svelte/guide/cell-selection.md‎

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,27 @@ The cell selection feature keeps track of spreadsheet-style rectangular selectio
3838

3939
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.
4040

41-
- `table.state.cellSelection` - returns the cell selection state reactively (selected by the `createTable` selector)
41+
- `table.atoms.cellSelection.get()` - returns the current cell selection state and participates in Svelte tracking when read in a template or rune
4242
- `getSelectedCellCount()` - returns how many cells are selected
4343
- `getSelectedCellIds()` - returns the ids of every selected cell
4444
- `getCellSelectionRowIds()` / `getCellSelectionColumnIds()` - returns the rows and columns the selection touches
4545
- `getSelectedCellRangesData()` - returns each selected rectangle's values as a row-major grid
4646

4747
```ts
48-
console.log(table.state.cellSelection) //get the cell selection state
48+
console.log(table.atoms.cellSelection.get()) //get the cell selection state
4949
console.log(table.getSelectedCellCount()) //3
5050
console.log(table.getSelectedCellIds()) //['0_firstName', '0_lastName', '1_firstName']
5151
console.log(table.getSelectedCellRangesData()) //[[['Tanner', 'Linsley'], ['Kevin', 'Vandy']]]
5252
```
5353

54-
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.
54+
Use the same atom read in markup or a native derived value:
55+
56+
```ts
57+
const cellSelection = $derived(table.atoms.cellSelection.get())
58+
const selectedRangeCount = $derived(cellSelection.length)
59+
```
60+
61+
Outside a tracked context, `table.atoms.cellSelection.get()` is simply the current snapshot.
5562

5663
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.
5764

@@ -313,8 +320,8 @@ Because a reorder can widen a selection onto columns the user never picked, some
313320
let isFirstColumnLayout = true
314321

315322
$effect(() => {
316-
// read the atoms, not table.state: the selector rebuilds that object on every
317-
// selected slice change, so reading it here would re-fire on every selection
323+
// Read only the layout atoms. A full table.store.get() read would re-run for
324+
// every table state change, including selection changes.
318325
void table.atoms.columnOrder.get()
319326
void table.atoms.columnPinning.get()
320327
void table.atoms.columnVisibility.get()
@@ -343,21 +350,8 @@ const table = createTable({
343350

344351
### Performance
345352

346-
Svelte's runes track these reads and the compiler updates only the DOM nodes
347-
that actually changed, so the example renders its cells plainly.
353+
Svelte's runes track atom reads and the compiler updates only the DOM nodes that actually changed, so the example renders its cells plainly.
348354

349-
Measured on a table with a thousand rows and twelve columns, a drag updates in
350-
roughly 16ms per move with plain reads.
351-
352-
`subscribeTable` is available if you want an explicit fine-grained subscription:
353-
354-
```svelte
355-
const selected = subscribeTable( table.atoms.cellSelection, (ranges) =>
356-
ranges.length, ) // read it as selected.current
357-
```
355+
Measured on a table with a thousand rows and twelve columns, a drag updates in roughly 16ms per move with plain reads.
358356

359-
One trap worth knowing: do not read `table.state` inside an `$effect` that also
360-
writes selection state. The selector rebuilds that object whenever any selected
361-
slice changes, so an effect meant to watch the column layout will re-fire on
362-
every selection change and clear the selection you just made. Read
363-
`table.atoms.<slice>.get()` instead.
357+
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.<slice>.get()` values instead.

‎docs/framework/svelte/guide/column-filtering.md‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ Since the column filter state is an array of objects, you can have multiple colu
138138
139139
#### Accessing Column Filter State
140140
141-
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.
141+
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`.
142142
143143
```ts
144144
const table = createTable({
@@ -148,8 +148,11 @@ const table = createTable({
148148
//...
149149
})
150150

151-
table.state.columnFilters // reactive read
152-
table.atoms.columnFilters.get() // snapshot read in event handlers
151+
const columnFilters = $derived(table.atoms.columnFilters.get())
152+
153+
const logCurrentColumnFilters = () => {
154+
console.log(table.atoms.columnFilters.get())
155+
}
153156
```
154157

155158
However, if you need access to the column filter state outside of the table, you can "control" the column filter state like down below.

‎docs/framework/svelte/guide/column-resizing.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ TanStack Table keeps track of a `columnResizing` state object that you can use t
148148
<div
149149
class="resize-indicator"
150150
style:transform={header.column.getIsResizing()
151-
? `translateX(${table.state.columnResizing.deltaOffset ?? 0}px)`
151+
? `translateX(${table.atoms.columnResizing.get().deltaOffset ?? 0}px)`
152152
: ''}
153153
></div>
154154
```
@@ -253,4 +253,4 @@ Svelte 5's fine-grained reactivity means a resize does not re-render whole compo
253253
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 `<table>` element (`<table style={tableStyle}>`). `header.getSize()` reads the rune-backed `columnSizing` atom, so a drag re-runs only this derived and its single attribute effect.
254254
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.
255255

256-
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.
256+
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.

‎docs/framework/svelte/guide/composable-tables.md‎

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -276,13 +276,9 @@ Create each table with `createAppTable`. In Svelte 5, pass reactive data through
276276
debugTable: true,
277277
})
278278
279-
let sorting = $derived(table.state.sorting)
280-
let columnFilters = $derived(table.state.columnFilters)
281-
282-
const rows = $derived.by(() => {
283-
JSON.stringify(table.state)
284-
return table.getRowModel().rows
285-
})
279+
const sorting = $derived(table.atoms.sorting.get())
280+
const columnFilters = $derived(table.atoms.columnFilters.get())
281+
const rows = $derived(table.getRowModel().rows)
286282
</script>
287283
```
288284

‎docs/framework/svelte/guide/custom-features.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,10 +330,10 @@ const table = createTable({
330330

331331
### Step 5: Use the Feature in Your Application
332332

333-
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.
333+
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.
334334

335335
```svelte
336-
{@const density = table.state.density}
336+
{@const density = table.atoms.density.get()}
337337
<td
338338
style:padding={density === 'sm' ? '4px' : density === 'md' ? '8px' : '16px'}
339339
style:transition="padding 0.2s"

0 commit comments

Comments
 (0)