@tanstack/svelte-table 9.0.0-beta.7 → 9.0.0-beta.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/FlexRender.svelte +15 -1
- package/dist/createTable.svelte.d.ts +26 -36
- package/dist/createTable.svelte.js +27 -32
- package/dist/createTableHook.svelte.d.ts +51 -18
- package/dist/createTableHook.svelte.js +18 -10
- package/dist/experimental-worker-plugin.d.ts +1 -0
- package/dist/experimental-worker-plugin.js +1 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +0 -1
- package/dist/reactivity.svelte.d.ts +3 -1
- package/dist/reactivity.svelte.js +6 -1
- package/dist/render-component.js +4 -0
- package/package.json +16 -9
- package/skills/create-table-hook/SKILL.md +168 -0
- package/skills/getting-started/SKILL.md +172 -0
- package/skills/migrate-v8-to-v9/SKILL.md +201 -0
- package/skills/table-state/SKILL.md +262 -0
- package/skills/with-tanstack-query/SKILL.md +149 -0
- package/skills/with-tanstack-virtual/SKILL.md +150 -0
- package/dist/subscribe.d.ts +0 -24
- package/dist/subscribe.js +0 -4
- package/skills/svelte/client-to-server/SKILL.md +0 -238
- package/skills/svelte/compose-with-tanstack-form/SKILL.md +0 -295
- package/skills/svelte/compose-with-tanstack-pacer/SKILL.md +0 -176
- package/skills/svelte/compose-with-tanstack-query/SKILL.md +0 -299
- package/skills/svelte/compose-with-tanstack-store/SKILL.md +0 -277
- package/skills/svelte/compose-with-tanstack-virtual/SKILL.md +0 -286
- package/skills/svelte/getting-started/SKILL.md +0 -340
- package/skills/svelte/migrate-v8-to-v9/SKILL.md +0 -256
- package/skills/svelte/production-readiness/SKILL.md +0 -256
- package/skills/svelte/table-state/SKILL.md +0 -441
- package/src/AppCell.svelte +0 -13
- package/src/AppHeader.svelte +0 -13
- package/src/AppTable.svelte +0 -11
- package/src/FlexRender.svelte +0 -103
- package/src/context-keys.ts +0 -3
- package/src/createTable.svelte.ts +0 -137
- package/src/createTableHook.svelte.ts +0 -639
- package/src/createTableState.svelte.ts +0 -30
- package/src/flex-render.ts +0 -3
- package/src/global.d.ts +0 -1
- package/src/index.ts +0 -21
- package/src/merge-objects.ts +0 -79
- package/src/reactivity.svelte.ts +0 -118
- package/src/render-component.ts +0 -107
- package/src/static-functions.ts +0 -1
- package/src/subscribe.ts +0 -46
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: table-state
|
|
3
|
+
description: >
|
|
4
|
+
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.
|
|
5
|
+
metadata:
|
|
6
|
+
type: framework
|
|
7
|
+
library: '@tanstack/svelte-table'
|
|
8
|
+
framework: svelte
|
|
9
|
+
library_version: '9.0.0-beta.70'
|
|
10
|
+
requires:
|
|
11
|
+
- '@tanstack/table-core#core'
|
|
12
|
+
- getting-started
|
|
13
|
+
sources:
|
|
14
|
+
- 'TanStack/table:docs/framework/svelte/guide/table-state.md'
|
|
15
|
+
- 'TanStack/table:docs/framework/svelte/guide/pagination.md'
|
|
16
|
+
- 'TanStack/table:examples/svelte/basic-external-state'
|
|
17
|
+
- 'TanStack/table:packages/svelte-table/src/createTable.svelte.ts'
|
|
18
|
+
- 'TanStack/table:packages/svelte-table/src/createTableState.svelte.ts'
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
This skill builds on `@tanstack/table-core#core` and `getting-started`. Read them first for table ownership and Svelte construction.
|
|
22
|
+
|
|
23
|
+
## State Mental Model
|
|
24
|
+
|
|
25
|
+
TanStack Table is primarily a state coordinator. Keep state internal unless another system must read, persist, or drive it. Without `initialState`, `atoms`, `state`, or `on[State]Change`, the table owns all registered slices.
|
|
26
|
+
|
|
27
|
+
- `table.baseAtoms` are internal writable atoms initialized from resolved initial state.
|
|
28
|
+
- `table.atoms` are readonly derived atoms for the active owner of each registered slice.
|
|
29
|
+
- `table.store` is the readonly flat store assembled from those atoms.
|
|
30
|
+
|
|
31
|
+
The Svelte adapter bridges TanStack Store dependency tracking into Svelte runes. `table.atoms.<slice>.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.
|
|
32
|
+
|
|
33
|
+
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.
|
|
34
|
+
|
|
35
|
+
## Setup
|
|
36
|
+
|
|
37
|
+
Keep state internal unless another subsystem needs to own it. Read only the state slices a component needs.
|
|
38
|
+
|
|
39
|
+
```svelte
|
|
40
|
+
<script lang="ts">
|
|
41
|
+
import {
|
|
42
|
+
createTable,
|
|
43
|
+
rowPaginationFeature,
|
|
44
|
+
tableFeatures,
|
|
45
|
+
} from '@tanstack/svelte-table'
|
|
46
|
+
|
|
47
|
+
const features = tableFeatures({ rowPaginationFeature })
|
|
48
|
+
const columns = [{ accessorKey: 'name' }]
|
|
49
|
+
let data = $state([{ name: 'Ada' }])
|
|
50
|
+
|
|
51
|
+
const table = createTable({
|
|
52
|
+
features,
|
|
53
|
+
columns,
|
|
54
|
+
get data() {
|
|
55
|
+
return data
|
|
56
|
+
},
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
const pagination = $derived(table.atoms.pagination.get())
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<button onclick={() => table.nextPage()} disabled={!table.getCanNextPage()}>
|
|
63
|
+
Page {pagination.pageIndex + 1}
|
|
64
|
+
</button>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Core Patterns
|
|
68
|
+
|
|
69
|
+
### Read narrow or complete state
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
const pagination = $derived(table.atoms.pagination.get())
|
|
73
|
+
const pageIndex = $derived(table.atoms.pagination.get().pageIndex)
|
|
74
|
+
const rows = $derived(table.getRowModel().rows)
|
|
75
|
+
const stateJson = $derived(JSON.stringify(table.store.get(), null, 2))
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
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.
|
|
79
|
+
|
|
80
|
+
### Control a slice with value-or-updater semantics
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import type { PaginationState, Updater } from '@tanstack/svelte-table'
|
|
84
|
+
|
|
85
|
+
let pagination = $state<PaginationState>({ pageIndex: 0, pageSize: 20 })
|
|
86
|
+
const updatePagination = (next: Updater<PaginationState>) => {
|
|
87
|
+
pagination = typeof next === 'function' ? next(pagination) : next
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Pass a getter-backed `state.pagination` and `onPaginationChange: updatePagination` to `createTable`.
|
|
92
|
+
|
|
93
|
+
### Reduce boilerplate with `createTableState`
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import {
|
|
97
|
+
createTable,
|
|
98
|
+
createTableState,
|
|
99
|
+
rowPaginationFeature,
|
|
100
|
+
tableFeatures,
|
|
101
|
+
type PaginationState,
|
|
102
|
+
} from '@tanstack/svelte-table'
|
|
103
|
+
|
|
104
|
+
const features = tableFeatures({ rowPaginationFeature })
|
|
105
|
+
const columns = [{ accessorKey: 'name' }]
|
|
106
|
+
const data = [{ name: 'Ada' }]
|
|
107
|
+
const [pagination, setPagination] = createTableState<PaginationState>({
|
|
108
|
+
pageIndex: 0,
|
|
109
|
+
pageSize: 20,
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
const table = createTable({
|
|
113
|
+
features,
|
|
114
|
+
columns,
|
|
115
|
+
data,
|
|
116
|
+
state: {
|
|
117
|
+
get pagination() {
|
|
118
|
+
return pagination()
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
onPaginationChange: setPagination,
|
|
122
|
+
})
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
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`.
|
|
126
|
+
|
|
127
|
+
## Choose State Ownership
|
|
128
|
+
|
|
129
|
+
Use one owner per slice:
|
|
130
|
+
|
|
131
|
+
- Prefer internal state plus feature APIs for table-local interaction.
|
|
132
|
+
- Use `initialState` for starting/reset values; changing it later does not reset state.
|
|
133
|
+
- Use Svelte `$state`, a getter-backed `state` entry, and the matching callback for normal Svelte-owned controlled state.
|
|
134
|
+
- Use a stable external atom in `atoms` for state shared as a raw TanStack Store atom. Do not also add its change callback.
|
|
135
|
+
|
|
136
|
+
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.
|
|
137
|
+
|
|
138
|
+
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.<slice>.get()` wrapper.
|
|
139
|
+
|
|
140
|
+
## Initialize, Update, and Reset
|
|
141
|
+
|
|
142
|
+
Prefer `setSorting`, `nextPage`, `toggleVisibility`, `toggleSelected`, and other feature APIs over direct state writes. Write a base atom only for rare internal-state needs; write the external atom when `atoms.<slice>` owns it.
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
table.resetSorting()
|
|
146
|
+
table.resetPagination()
|
|
147
|
+
table.resetPagination(true)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Feature resets use `table.initialState` unless `true` requests the feature default and can flow to external owners. Core `table.reset()` resets internal base atoms only. Use feature types such as `PaginationState` for a slice and `TableState<typeof features>` for the complete registered state.
|
|
151
|
+
|
|
152
|
+
## Common Mistakes
|
|
153
|
+
|
|
154
|
+
### HIGH Keeping removed adapter selectors
|
|
155
|
+
|
|
156
|
+
Wrong:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
const table = createTable(options, (state) => state.pagination)
|
|
160
|
+
const pageIndex = table.state.pageIndex
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Correct:
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
const table = createTable(options)
|
|
167
|
+
const pagination = $derived(table.atoms.pagination.get())
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
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.
|
|
171
|
+
|
|
172
|
+
Source: `docs/framework/svelte/guide/migrating.md`
|
|
173
|
+
|
|
174
|
+
### HIGH Controlling without writing back
|
|
175
|
+
|
|
176
|
+
Wrong:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
const options = { state: { pagination }, onPaginationChange: console.log }
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Correct:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
const options = {
|
|
186
|
+
state: {
|
|
187
|
+
get pagination() {
|
|
188
|
+
return pagination
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
onPaginationChange: updatePagination,
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
A controlled slice is frozen unless every updater is resolved into the owning rune.
|
|
196
|
+
|
|
197
|
+
Source: `docs/framework/svelte/guide/table-state.md`
|
|
198
|
+
|
|
199
|
+
### HIGH Snapshotting outside tracking
|
|
200
|
+
|
|
201
|
+
Wrong:
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
const pageIndex = table.store.get().pagination.pageIndex
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Correct inside a component:
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
const pageIndex = $derived(table.atoms.pagination.get().pageIndex)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
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.
|
|
214
|
+
|
|
215
|
+
Source: `packages/svelte-table/src/createTable.svelte.ts`
|
|
216
|
+
|
|
217
|
+
### MEDIUM Declaring one slice in two owners
|
|
218
|
+
|
|
219
|
+
Wrong:
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
const options = { initialState: { pagination: start }, state: { pagination } }
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Correct:
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
const options = {
|
|
229
|
+
state: {
|
|
230
|
+
get pagination() {
|
|
231
|
+
return pagination
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Controlled `atoms` or `state` wins over `initialState`; choose one owner per slice.
|
|
238
|
+
|
|
239
|
+
Source: `docs/framework/svelte/guide/table-state.md`
|
|
240
|
+
|
|
241
|
+
### MEDIUM Fighting automatic page reset
|
|
242
|
+
|
|
243
|
+
Wrong:
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
table.setPageIndex(4)
|
|
247
|
+
data = filteredData
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Correct:
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
const options = { autoResetPageIndex: false }
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Client row-model changes reset the page by default; disable it only when the application handles invalid empty pages.
|
|
257
|
+
|
|
258
|
+
Source: `docs/framework/svelte/guide/pagination.md`
|
|
259
|
+
|
|
260
|
+
## API Discovery
|
|
261
|
+
|
|
262
|
+
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.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: with-tanstack-query
|
|
3
|
+
description: >
|
|
4
|
+
Compose Svelte Query with Svelte Table manual filtering, sorting, and pagination using reactive query inputs, query-result data getters, server counts, and a single source of server-data truth.
|
|
5
|
+
metadata:
|
|
6
|
+
type: composition
|
|
7
|
+
library: '@tanstack/svelte-table'
|
|
8
|
+
framework: svelte
|
|
9
|
+
library_version: '9.0.0-beta.70'
|
|
10
|
+
requires:
|
|
11
|
+
- '@tanstack/table-core#client-vs-server'
|
|
12
|
+
- getting-started
|
|
13
|
+
- table-state
|
|
14
|
+
sources:
|
|
15
|
+
- 'TanStack/table:examples/svelte/with-tanstack-query'
|
|
16
|
+
- 'TanStack/table:docs/framework/svelte/guide/pagination.md'
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
This skill builds on `@tanstack/table-core#client-vs-server`, `getting-started`, and `table-state`. Decide which row-processing stages the server owns before composing Query.
|
|
20
|
+
|
|
21
|
+
## Setup
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { createQuery, keepPreviousData } from '@tanstack/svelte-query'
|
|
25
|
+
import {
|
|
26
|
+
createTable,
|
|
27
|
+
rowPaginationFeature,
|
|
28
|
+
tableFeatures,
|
|
29
|
+
} from '@tanstack/svelte-table'
|
|
30
|
+
|
|
31
|
+
const features = tableFeatures({ rowPaginationFeature })
|
|
32
|
+
let pagination = $state({ pageIndex: 0, pageSize: 20 })
|
|
33
|
+
const defaultData: Array<{ name: string }> = []
|
|
34
|
+
const dataQuery = createQuery<{
|
|
35
|
+
rows: Array<{ name: string }>
|
|
36
|
+
rowCount: number
|
|
37
|
+
}>(() => ({
|
|
38
|
+
queryKey: ['people', pagination.pageIndex, pagination.pageSize],
|
|
39
|
+
queryFn: () =>
|
|
40
|
+
fetch(
|
|
41
|
+
`/api/people?page=${pagination.pageIndex}&size=${pagination.pageSize}`,
|
|
42
|
+
).then((r) => r.json()),
|
|
43
|
+
placeholderData: keepPreviousData,
|
|
44
|
+
}))
|
|
45
|
+
const table = createTable({
|
|
46
|
+
features,
|
|
47
|
+
columns,
|
|
48
|
+
get data() {
|
|
49
|
+
return dataQuery.data?.rows ?? defaultData
|
|
50
|
+
},
|
|
51
|
+
get rowCount() {
|
|
52
|
+
return dataQuery.data?.rowCount ?? 0
|
|
53
|
+
},
|
|
54
|
+
manualPagination: true,
|
|
55
|
+
state: {
|
|
56
|
+
get pagination() {
|
|
57
|
+
return pagination
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
onPaginationChange: (next) => {
|
|
61
|
+
pagination = typeof next === 'function' ? next(pagination) : next
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Core Patterns
|
|
67
|
+
|
|
68
|
+
### Put every server-owned stage in the query key
|
|
69
|
+
|
|
70
|
+
If sorting or filtering is manual too, control those slices and include their serializable values in `queryKey`. Return data already processed in that same order.
|
|
71
|
+
|
|
72
|
+
### Keep Query as server-data owner
|
|
73
|
+
|
|
74
|
+
Expose `dataQuery.data` through Table getters. Copy it into `$state` only when the application explicitly owns an editable draft and defines cache synchronization.
|
|
75
|
+
|
|
76
|
+
## Common Mistakes
|
|
77
|
+
|
|
78
|
+
### HIGH Building a non-reactive query
|
|
79
|
+
|
|
80
|
+
Wrong:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
const query = createQuery({
|
|
84
|
+
queryKey: ['people', pagination.pageIndex],
|
|
85
|
+
queryFn,
|
|
86
|
+
})
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Correct:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
const query = createQuery(() => ({
|
|
93
|
+
queryKey: ['people', pagination.pageIndex],
|
|
94
|
+
queryFn,
|
|
95
|
+
}))
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The options function lets Svelte Query track the rune read and refetch on page changes.
|
|
99
|
+
|
|
100
|
+
Source: `examples/svelte/with-tanstack-query/src/App.svelte`
|
|
101
|
+
|
|
102
|
+
### HIGH Expecting manual mode to fetch
|
|
103
|
+
|
|
104
|
+
Wrong:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
const options = { manualPagination: true }
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Correct:
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
const options = {
|
|
114
|
+
manualPagination: true,
|
|
115
|
+
get data() {
|
|
116
|
+
return dataQuery.data?.rows ?? defaultData
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Manual mode only bypasses Table pagination; Query or application code performs the request. Hoist `defaultData` instead of creating a new `[]` from a repeatedly evaluated getter.
|
|
122
|
+
|
|
123
|
+
Source: `docs/framework/svelte/guide/pagination.md`
|
|
124
|
+
|
|
125
|
+
### HIGH Omitting total counts
|
|
126
|
+
|
|
127
|
+
Wrong:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
const options = { manualPagination: true, data: pageRows }
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Correct:
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
const options = {
|
|
137
|
+
manualPagination: true,
|
|
138
|
+
data: pageRows,
|
|
139
|
+
rowCount: response.rowCount,
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Table cannot derive navigation limits from one server page; provide `rowCount` or `pageCount`.
|
|
144
|
+
|
|
145
|
+
Source: `docs/framework/svelte/guide/pagination.md`
|
|
146
|
+
|
|
147
|
+
## API Discovery
|
|
148
|
+
|
|
149
|
+
Inspect `node_modules/@tanstack/svelte-table/dist/index.d.ts` for adapter APIs and installed `@tanstack/svelte-query/dist/` for the exact Query version. Table manual-stage options live in the matching core feature source.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: with-tanstack-virtual
|
|
3
|
+
description: >
|
|
4
|
+
Virtualize Svelte Table final row or column models with reactive counts and scroll targets, stable keys, dynamic measurement, absolute transforms, sticky regions, grid/flex sizing, and infinite data.
|
|
5
|
+
metadata:
|
|
6
|
+
type: composition
|
|
7
|
+
library: '@tanstack/svelte-table'
|
|
8
|
+
framework: svelte
|
|
9
|
+
library_version: '9.0.0-beta.70'
|
|
10
|
+
requires:
|
|
11
|
+
- '@tanstack/table-core#core'
|
|
12
|
+
- getting-started
|
|
13
|
+
- table-state
|
|
14
|
+
sources:
|
|
15
|
+
- 'TanStack/table:docs/framework/svelte/guide/virtualization.md'
|
|
16
|
+
- 'TanStack/table:examples/svelte/virtualized-rows'
|
|
17
|
+
- 'TanStack/table:examples/svelte/virtualized-columns'
|
|
18
|
+
- 'TanStack/table:examples/svelte/virtualized-infinite-scrolling'
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
This skill builds on `@tanstack/table-core#core`, `getting-started`, and `table-state`. Virtual is a rendering layer over Table’s final model, never a `tableFeatures` plugin.
|
|
22
|
+
|
|
23
|
+
## Setup
|
|
24
|
+
|
|
25
|
+
```svelte
|
|
26
|
+
<script lang="ts">
|
|
27
|
+
import { get } from 'svelte/store'
|
|
28
|
+
import { createVirtualizer } from '@tanstack/svelte-virtual'
|
|
29
|
+
|
|
30
|
+
let scrollElement = $state<HTMLDivElement>()
|
|
31
|
+
const rows = $derived(table.getRowModel().rows)
|
|
32
|
+
const rowVirtualizer = createVirtualizer({
|
|
33
|
+
count: rows.length,
|
|
34
|
+
getScrollElement: () => scrollElement ?? null,
|
|
35
|
+
estimateSize: () => 34,
|
|
36
|
+
getItemKey: (index) => rows[index]!.id,
|
|
37
|
+
overscan: 5,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// The store adapter does not track getter options. Push reactive inputs.
|
|
41
|
+
$effect(() => {
|
|
42
|
+
get(rowVirtualizer).setOptions({
|
|
43
|
+
count: rows.length,
|
|
44
|
+
getScrollElement: () => scrollElement ?? null,
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<div
|
|
50
|
+
bind:this={scrollElement}
|
|
51
|
+
style="height: 500px; overflow: auto; position: relative"
|
|
52
|
+
>
|
|
53
|
+
<div style:height={`${$rowVirtualizer.getTotalSize()}px`}>
|
|
54
|
+
{#each $rowVirtualizer.getVirtualItems() as item (item.key)}
|
|
55
|
+
<div style={`position:absolute;transform:translateY(${item.start}px)`}>
|
|
56
|
+
{rows[item.index].id}
|
|
57
|
+
</div>
|
|
58
|
+
{/each}
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Core Patterns
|
|
64
|
+
|
|
65
|
+
### Virtualize visible models
|
|
66
|
+
|
|
67
|
+
Use `table.getRowModel().rows` for rows and `table.getVisibleLeafColumns()` for columns. Recompute counts when filtering, sorting, expansion, or visibility changes.
|
|
68
|
+
|
|
69
|
+
### Make CSS geometry agree with measurement
|
|
70
|
+
|
|
71
|
+
Use one scroll container, a total-size spacer, positioned items, and either fixed estimates or `measureElement`. For semantic tables with dynamic rows, follow the maintained grid/flex examples rather than assuming native table layout will honor transforms.
|
|
72
|
+
|
|
73
|
+
### Fetch before the virtual end
|
|
74
|
+
|
|
75
|
+
In infinite scrolling, compare the last virtual item with fetched row count, then request the next Query page only when more server rows exist and no fetch is active.
|
|
76
|
+
|
|
77
|
+
## Common Mistakes
|
|
78
|
+
|
|
79
|
+
### HIGH Virtualizing raw data
|
|
80
|
+
|
|
81
|
+
Wrong:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
const rows = data
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Correct:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
const rows = $derived(table.getRowModel().rows)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Raw data ignores Table filtering, sorting, grouping, expansion, and pagination decisions.
|
|
94
|
+
|
|
95
|
+
Source: `examples/svelte/virtualized-rows/src/App.svelte`
|
|
96
|
+
|
|
97
|
+
### HIGH Expecting getter options to stay reactive
|
|
98
|
+
|
|
99
|
+
Wrong:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
const virtualizer = createVirtualizer({
|
|
103
|
+
get count() {
|
|
104
|
+
return rows.length
|
|
105
|
+
},
|
|
106
|
+
getScrollElement,
|
|
107
|
+
})
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Correct:
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
const virtualizer = createVirtualizer({ count: rows.length, getScrollElement })
|
|
114
|
+
$effect(() => {
|
|
115
|
+
get(virtualizer).setOptions({
|
|
116
|
+
count: rows.length,
|
|
117
|
+
getScrollElement,
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`createVirtualizer` returns a Svelte store, and its adapter does not track getter options. Push rune-derived counts and the bound scroll element with `$effect` and `get(store).setOptions(...)`.
|
|
123
|
+
|
|
124
|
+
Source: `examples/svelte/virtualized-rows/src/App.svelte`
|
|
125
|
+
|
|
126
|
+
### HIGH Omitting the geometry contract
|
|
127
|
+
|
|
128
|
+
Wrong:
|
|
129
|
+
|
|
130
|
+
```svelte
|
|
131
|
+
{#each rowVirtualizer.getVirtualItems() as item}<div>
|
|
132
|
+
{rows[item.index].id}
|
|
133
|
+
</div>{/each}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Correct:
|
|
137
|
+
|
|
138
|
+
```svelte
|
|
139
|
+
<div style:height={`${$rowVirtualizer.getTotalSize()}px`}>
|
|
140
|
+
<div style="position:absolute"></div>
|
|
141
|
+
</div>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Virtual supplies ranges and measurements, not spacer height, transforms, sticky regions, or column widths. In markup, call virtualizer methods through the store auto-subscription (`$rowVirtualizer`); use `get(rowVirtualizer)` in script code.
|
|
145
|
+
|
|
146
|
+
Source: `docs/framework/svelte/guide/virtualization.md`
|
|
147
|
+
|
|
148
|
+
## API Discovery
|
|
149
|
+
|
|
150
|
+
Inspect installed `@tanstack/svelte-table/dist/` for Table APIs and `@tanstack/svelte-virtual/dist/` for the exact virtualizer options. Use the maintained Svelte examples for layout combinations.
|
package/dist/subscribe.d.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { useSelector } from '@tanstack/svelte-store';
|
|
2
|
-
import type { Atom, ReadonlyAtom, ReadonlyStore, Store } from '@tanstack/svelte-store';
|
|
3
|
-
export type SubscribeSource<TValue> = Atom<TValue> | ReadonlyAtom<TValue> | Store<TValue> | ReadonlyStore<TValue>;
|
|
4
|
-
/**
|
|
5
|
-
* Creates a fine-grained Svelte subscription to a TanStack Store source.
|
|
6
|
-
*
|
|
7
|
-
* Pass a table atom or store and optionally project it with a selector. The
|
|
8
|
-
* returned selector store exposes `.current`, making it useful for reading
|
|
9
|
-
* focused table state outside the broad `createTable` selector.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```svelte
|
|
13
|
-
* <script lang="ts">
|
|
14
|
-
* const selected = subscribeTable(
|
|
15
|
-
* table.atoms.rowSelection,
|
|
16
|
-
* (rowSelection) => rowSelection[row.id],
|
|
17
|
-
* )
|
|
18
|
-
* </script>
|
|
19
|
-
*
|
|
20
|
-
* <input type="checkbox" checked={!!selected.current} />
|
|
21
|
-
* ```
|
|
22
|
-
*/
|
|
23
|
-
export declare function subscribeTable<TSourceValue>(source: SubscribeSource<TSourceValue>): ReturnType<typeof useSelector<TSourceValue>>;
|
|
24
|
-
export declare function subscribeTable<TSourceValue, TSelected>(source: SubscribeSource<TSourceValue>, selector: (state: TSourceValue) => TSelected): ReturnType<typeof useSelector<TSourceValue, TSelected>>;
|
package/dist/subscribe.js
DELETED