@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
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: svelte/compose-with-tanstack-virtual
|
|
3
|
-
description: >
|
|
4
|
-
`@tanstack/svelte-table` does not include virtualization — pair it with
|
|
5
|
-
`@tanstack/svelte-virtual`. Use `createVirtualizer({ count, estimateSize, getScrollElement,
|
|
6
|
-
... })`, feed `table.getRowModel().rows.length` as `count`, render only
|
|
7
|
-
`$rowVirtualizer.getVirtualItems()`, position rows with `transform: translateY(...)` and a
|
|
8
|
-
container of `getTotalSize()`. Use `measureElement` actions for dynamic row heights. Svelte 5+
|
|
9
|
-
only — `$state` for refs, `$effect` to sync count.
|
|
10
|
-
type: composition
|
|
11
|
-
library: tanstack-table
|
|
12
|
-
framework: svelte
|
|
13
|
-
library_version: '9.0.0-alpha.48'
|
|
14
|
-
requires:
|
|
15
|
-
- svelte/table-state
|
|
16
|
-
- row-expanding
|
|
17
|
-
sources:
|
|
18
|
-
- TanStack/table:docs/guide/virtualization.md
|
|
19
|
-
- TanStack/table:examples/svelte/virtualized-rows/
|
|
20
|
-
- TanStack/table:examples/svelte/virtualized-columns/
|
|
21
|
-
- TanStack/table:examples/svelte/virtualized-infinite-scrolling/
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
# Compose with TanStack Virtual (Svelte)
|
|
25
|
-
|
|
26
|
-
TanStack Table is **not** a virtualizer. For lists / grids past a few thousand rows (or with
|
|
27
|
-
heavy per-row markup), pair it with `@tanstack/svelte-virtual`.
|
|
28
|
-
|
|
29
|
-
## Install
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
pnpm add @tanstack/svelte-virtual
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Core mental model
|
|
36
|
-
|
|
37
|
-
- TanStack Table gives you `rows: row[]` (already filtered / sorted / paged / grouped).
|
|
38
|
-
- TanStack Virtual takes `count` (the length) and returns `virtualItems` (the slice currently
|
|
39
|
-
in view).
|
|
40
|
-
- You render only those virtual items, absolutely positioned, inside a container sized to
|
|
41
|
-
`getTotalSize()` pixels.
|
|
42
|
-
|
|
43
|
-
`createVirtualizer` returns a Svelte store. Read its current value with `$rowVirtualizer` or
|
|
44
|
-
`get(rowVirtualizer)` (from `svelte/store`).
|
|
45
|
-
|
|
46
|
-
## Basic row virtualization
|
|
47
|
-
|
|
48
|
-
```svelte
|
|
49
|
-
<script lang="ts">
|
|
50
|
-
import {
|
|
51
|
-
columnSizingFeature,
|
|
52
|
-
createSortedRowModel,
|
|
53
|
-
createTable,
|
|
54
|
-
rowSortingFeature,
|
|
55
|
-
sortFns,
|
|
56
|
-
tableFeatures,
|
|
57
|
-
FlexRender,
|
|
58
|
-
} from '@tanstack/svelte-table'
|
|
59
|
-
import { createVirtualizer } from '@tanstack/svelte-virtual'
|
|
60
|
-
import { get } from 'svelte/store'
|
|
61
|
-
|
|
62
|
-
const features = tableFeatures({ columnSizingFeature, rowSortingFeature })
|
|
63
|
-
|
|
64
|
-
let data = $state<Person[]>(makeData(200_000))
|
|
65
|
-
|
|
66
|
-
const table = createTable({
|
|
67
|
-
features,
|
|
68
|
-
rowModels: { sortedRowModel: createSortedRowModel(sortFns) },
|
|
69
|
-
columns,
|
|
70
|
-
get data() {
|
|
71
|
-
return data
|
|
72
|
-
},
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
let tableContainerRef = $state<HTMLDivElement | undefined>(undefined)
|
|
76
|
-
|
|
77
|
-
const rows = $derived(table.getRowModel().rows)
|
|
78
|
-
|
|
79
|
-
const rowVirtualizer = createVirtualizer({
|
|
80
|
-
get count() {
|
|
81
|
-
return rows.length
|
|
82
|
-
},
|
|
83
|
-
estimateSize: () => 33,
|
|
84
|
-
getScrollElement: () => tableContainerRef ?? null,
|
|
85
|
-
overscan: 5,
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
// svelte-virtual's store adapter does not reactively track getter options;
|
|
89
|
-
// push updates explicitly when ref / count change.
|
|
90
|
-
$effect(() => {
|
|
91
|
-
if (tableContainerRef) {
|
|
92
|
-
get(rowVirtualizer).setOptions({
|
|
93
|
-
getScrollElement: () => tableContainerRef ?? null,
|
|
94
|
-
})
|
|
95
|
-
}
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
$effect(() => {
|
|
99
|
-
get(rowVirtualizer).setOptions({ count: rows.length })
|
|
100
|
-
})
|
|
101
|
-
</script>
|
|
102
|
-
|
|
103
|
-
<div
|
|
104
|
-
bind:this={tableContainerRef}
|
|
105
|
-
style="overflow: auto; position: relative; height: 800px;"
|
|
106
|
-
>
|
|
107
|
-
<table style="display: grid;">
|
|
108
|
-
<thead style="display: grid; position: sticky; top: 0; z-index: 1;">
|
|
109
|
-
{#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
|
|
110
|
-
<tr style="display: flex; width: 100%;">
|
|
111
|
-
{#each headerGroup.headers as header (header.id)}
|
|
112
|
-
<th style="display: flex; width: {header.getSize()}px;">
|
|
113
|
-
<FlexRender {header} />
|
|
114
|
-
</th>
|
|
115
|
-
{/each}
|
|
116
|
-
</tr>
|
|
117
|
-
{/each}
|
|
118
|
-
</thead>
|
|
119
|
-
<tbody
|
|
120
|
-
style="display: grid; position: relative; height: {$rowVirtualizer.getTotalSize()}px;"
|
|
121
|
-
>
|
|
122
|
-
{#each $rowVirtualizer.getVirtualItems() as virtualRow (virtualRow.index)}
|
|
123
|
-
{@const row = rows[virtualRow.index]}
|
|
124
|
-
<tr
|
|
125
|
-
data-index={virtualRow.index}
|
|
126
|
-
style="display: flex; position: absolute; transform: translateY({virtualRow.start}px); width: 100%;"
|
|
127
|
-
>
|
|
128
|
-
{#each row.getAllCells() as cell (cell.id)}
|
|
129
|
-
<td style="display: flex; width: {cell.column.getSize()}px;">
|
|
130
|
-
<FlexRender {cell} />
|
|
131
|
-
</td>
|
|
132
|
-
{/each}
|
|
133
|
-
</tr>
|
|
134
|
-
{/each}
|
|
135
|
-
</tbody>
|
|
136
|
-
</table>
|
|
137
|
-
</div>
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
Why `display: grid` / `flex` instead of native table layout? Because the rows are absolutely
|
|
141
|
-
positioned, the browser's native table layout algorithm can't size columns from non-flowing
|
|
142
|
-
rows. CSS layout takes over.
|
|
143
|
-
|
|
144
|
-
## Dynamic row heights (`measureElement`)
|
|
145
|
-
|
|
146
|
-
For variable row heights (multi-line cells, expanding rows), measure rendered nodes with the
|
|
147
|
-
virtualizer's `measureElement` API.
|
|
148
|
-
|
|
149
|
-
```svelte
|
|
150
|
-
<script lang="ts">
|
|
151
|
-
const rowVirtualizer = createVirtualizer({
|
|
152
|
-
get count() {
|
|
153
|
-
return rows.length
|
|
154
|
-
},
|
|
155
|
-
estimateSize: () => 33,
|
|
156
|
-
getScrollElement: () => tableContainerRef ?? null,
|
|
157
|
-
measureElement:
|
|
158
|
-
typeof window !== 'undefined' &&
|
|
159
|
-
navigator.userAgent.indexOf('Firefox') === -1
|
|
160
|
-
? (element) => element.getBoundingClientRect().height
|
|
161
|
-
: undefined,
|
|
162
|
-
overscan: 5,
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
// Svelte action wrapping the virtualizer's measure call.
|
|
166
|
-
function measureElement(node: HTMLTableRowElement) {
|
|
167
|
-
get(rowVirtualizer).measureElement(node)
|
|
168
|
-
}
|
|
169
|
-
</script>
|
|
170
|
-
|
|
171
|
-
<tr use:measureElement data-index={virtualRow.index} ...>...</tr>
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
`data-index` is required — the virtualizer uses it to map a measured element back to its
|
|
175
|
-
virtual item.
|
|
176
|
-
|
|
177
|
-
> Firefox measures table-border rows incorrectly. The above guards against measuring there and
|
|
178
|
-
> falls back to the estimate.
|
|
179
|
-
|
|
180
|
-
## Column virtualization
|
|
181
|
-
|
|
182
|
-
`createVirtualizer` with `horizontal: true` against `table.getVisibleLeafColumns()`. Same
|
|
183
|
-
pattern — only render `getVirtualItems()` cells per row, position with `translateX`.
|
|
184
|
-
|
|
185
|
-
```ts
|
|
186
|
-
const columnVirtualizer = createVirtualizer({
|
|
187
|
-
get count() {
|
|
188
|
-
return visibleColumns.length
|
|
189
|
-
},
|
|
190
|
-
estimateSize: (index) => visibleColumns[index].getSize(),
|
|
191
|
-
getScrollElement: () => tableContainerRef ?? null,
|
|
192
|
-
horizontal: true,
|
|
193
|
-
overscan: 3,
|
|
194
|
-
})
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
For combined row + column virtualization, render the row virtualizer's items, and inside each
|
|
198
|
-
row render the column virtualizer's items. See `examples/svelte/virtualized-columns/`.
|
|
199
|
-
|
|
200
|
-
## Infinite scroll (load more on near-bottom)
|
|
201
|
-
|
|
202
|
-
Subscribe to the virtualizer's `getVirtualItems()` and check the last one's index against your
|
|
203
|
-
total available count.
|
|
204
|
-
|
|
205
|
-
```ts
|
|
206
|
-
import { createInfiniteQuery } from '@tanstack/svelte-query'
|
|
207
|
-
|
|
208
|
-
const infiniteQuery = createInfiniteQuery(() => ({
|
|
209
|
-
queryKey: ['people-infinite'],
|
|
210
|
-
queryFn: ({ pageParam }) => fetchPeople({ cursor: pageParam, pageSize: 50 }),
|
|
211
|
-
initialPageParam: undefined as string | undefined,
|
|
212
|
-
getNextPageParam: (last) => last.nextCursor,
|
|
213
|
-
}))
|
|
214
|
-
|
|
215
|
-
const flatData = $derived(
|
|
216
|
-
infiniteQuery.data?.pages.flatMap((p) => p.rows) ?? [],
|
|
217
|
-
)
|
|
218
|
-
|
|
219
|
-
const table = createTable({
|
|
220
|
-
features: tableFeatures({}),
|
|
221
|
-
rowModels: {},
|
|
222
|
-
columns,
|
|
223
|
-
get data() {
|
|
224
|
-
return flatData
|
|
225
|
-
},
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
const rows = $derived(table.getRowModel().rows)
|
|
229
|
-
|
|
230
|
-
const rowVirtualizer = createVirtualizer({
|
|
231
|
-
get count() {
|
|
232
|
-
return rows.length
|
|
233
|
-
},
|
|
234
|
-
estimateSize: () => 33,
|
|
235
|
-
getScrollElement: () => tableContainerRef ?? null,
|
|
236
|
-
overscan: 10,
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
$effect(() => {
|
|
240
|
-
const items = $rowVirtualizer.getVirtualItems()
|
|
241
|
-
const last = items[items.length - 1]
|
|
242
|
-
if (
|
|
243
|
-
last &&
|
|
244
|
-
last.index >= rows.length - 1 &&
|
|
245
|
-
infiniteQuery.hasNextPage &&
|
|
246
|
-
!infiniteQuery.isFetchingNextPage
|
|
247
|
-
) {
|
|
248
|
-
infiniteQuery.fetchNextPage()
|
|
249
|
-
}
|
|
250
|
-
})
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
## Interaction with row expanding
|
|
254
|
-
|
|
255
|
-
If `rowExpandingFeature` is registered, `table.getRowModel().rows` already flattens expanded
|
|
256
|
-
sub-rows into a single sequential list. The virtualizer just sees a longer list — no special
|
|
257
|
-
handling needed.
|
|
258
|
-
|
|
259
|
-
For variable row heights driven by expand state, you'll want `measureElement` so the
|
|
260
|
-
container resizes when a row expands.
|
|
261
|
-
|
|
262
|
-
## Pagination vs. virtualization
|
|
263
|
-
|
|
264
|
-
Pick one. Virtualization is for "render all rows but render only the visible window".
|
|
265
|
-
Pagination is for "the user navigates discrete pages". Combining them usually means you don't
|
|
266
|
-
need either — drop pagination and let the virtualizer handle the rendering window.
|
|
267
|
-
|
|
268
|
-
## Common failure modes
|
|
269
|
-
|
|
270
|
-
- **Forgot to push `count` updates.** `svelte-virtual` does not auto-track `get count()` —
|
|
271
|
-
use `$effect` + `setOptions({ count })`.
|
|
272
|
-
- **Native table layout with virtualized rows.** Columns collapse because absolutely
|
|
273
|
-
positioned rows don't contribute to layout. Use `display: grid` / `flex`.
|
|
274
|
-
- **No `data-index` on `<tr>`.** `measureElement` can't map back to virtual items.
|
|
275
|
-
- **No `transform: translateY`.** Rows render at `top: 0` and stack visually.
|
|
276
|
-
- **Missing container `height`.** No overflow, no scroll, no virtualization.
|
|
277
|
-
- **Calling `get(rowVirtualizer).getVirtualItems()` in template.** Wrong access pattern;
|
|
278
|
-
use `$rowVirtualizer.getVirtualItems()` (store auto-subscribe) or be sure to
|
|
279
|
-
`import { get } from 'svelte/store'`.
|
|
280
|
-
- **Reimplementing windowing manually.** Don't.
|
|
281
|
-
|
|
282
|
-
## Related skills
|
|
283
|
-
|
|
284
|
-
- `tanstack-table/svelte/table-state` — `getRowModel()` and the reactivity model.
|
|
285
|
-
- `tanstack-table/core/row-expanding` — flattening sub-rows for virtualization.
|
|
286
|
-
- `tanstack-table/svelte/compose-with-tanstack-query` — infinite-scroll data source.
|
|
@@ -1,340 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: svelte/getting-started
|
|
3
|
-
description: >
|
|
4
|
-
End-to-end first-table journey for `@tanstack/svelte-table@9` on Svelte 5. Install the adapter,
|
|
5
|
-
declare `features` with `tableFeatures()`, register `rowModels` factories with their `*Fns`
|
|
6
|
-
parameters, build a typed column helper with both `TFeatures` and `TData` generics, instantiate
|
|
7
|
-
the table with `createTable(options)` using `$state` data and `get data()` reactive option getters,
|
|
8
|
-
and render with `FlexRender`. Svelte 5+ only — Svelte 3/4 must use v8.
|
|
9
|
-
type: lifecycle
|
|
10
|
-
library: tanstack-table
|
|
11
|
-
framework: svelte
|
|
12
|
-
library_version: '9.0.0-alpha.48'
|
|
13
|
-
requires:
|
|
14
|
-
- setup
|
|
15
|
-
- column-definitions
|
|
16
|
-
- state-management
|
|
17
|
-
- svelte/table-state
|
|
18
|
-
sources:
|
|
19
|
-
- TanStack/table:docs/installation.md
|
|
20
|
-
- TanStack/table:docs/framework/svelte/svelte-table.md
|
|
21
|
-
- TanStack/table:examples/svelte/basic-create-table/
|
|
22
|
-
- TanStack/table:examples/svelte/basic-app-table/
|
|
23
|
-
- TanStack/table:examples/svelte/basic-snippets/
|
|
24
|
-
- TanStack/table:packages/svelte-table/src/index.ts
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
# Getting Started — Svelte
|
|
28
|
-
|
|
29
|
-
A first working `@tanstack/svelte-table` v9 table from a blank Svelte 5 project. Read this
|
|
30
|
-
end-to-end before searching the docs — the v9 shape diverges enough from v8 (and from your
|
|
31
|
-
muscle memory) that skimming will produce broken code.
|
|
32
|
-
|
|
33
|
-
## CRITICAL: Svelte version
|
|
34
|
-
|
|
35
|
-
**`@tanstack/svelte-table@9` requires Svelte 5 or newer.** The adapter is built on runes
|
|
36
|
-
(`$state`, `$derived.by`, `$effect.pre`). If your project is on Svelte 3 or 4, do **one** of:
|
|
37
|
-
|
|
38
|
-
- Upgrade the project to Svelte 5, then install v9.
|
|
39
|
-
- Stay on `@tanstack/svelte-table@8` for that project.
|
|
40
|
-
|
|
41
|
-
There is no shim, no `/legacy` export, and no support path that runs v9 on Svelte 4.
|
|
42
|
-
|
|
43
|
-
## 1. Install
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
pnpm add @tanstack/svelte-table
|
|
47
|
-
# optional: external atoms / fine-grained selectors
|
|
48
|
-
pnpm add @tanstack/svelte-store
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
You do **not** install `@tanstack/table-core` separately — the Svelte adapter re-exports
|
|
52
|
-
everything you need (column helpers, feature objects, row-model factories, types).
|
|
53
|
-
|
|
54
|
-
## 2. Define `features` and `rowModels`
|
|
55
|
-
|
|
56
|
-
v9 is explicit. You opt in to every feature and every row model. The core row model is
|
|
57
|
-
included by default, so the minimum viable table is:
|
|
58
|
-
|
|
59
|
-
```ts
|
|
60
|
-
const features = tableFeatures({})
|
|
61
|
-
const rowModels = {}
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
For anything beyond a flat table, register the features you'll use **and** the matching
|
|
65
|
-
row-model factories. Row-model factories take `*Fns` registries as parameters:
|
|
66
|
-
|
|
67
|
-
```ts
|
|
68
|
-
import {
|
|
69
|
-
columnFilteringFeature,
|
|
70
|
-
createFilteredRowModel,
|
|
71
|
-
createPaginatedRowModel,
|
|
72
|
-
createSortedRowModel,
|
|
73
|
-
filterFns,
|
|
74
|
-
rowPaginationFeature,
|
|
75
|
-
rowSortingFeature,
|
|
76
|
-
sortFns,
|
|
77
|
-
tableFeatures,
|
|
78
|
-
} from '@tanstack/svelte-table'
|
|
79
|
-
|
|
80
|
-
const features = tableFeatures({
|
|
81
|
-
rowPaginationFeature,
|
|
82
|
-
rowSortingFeature,
|
|
83
|
-
columnFilteringFeature,
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
const rowModels = {
|
|
87
|
-
paginatedRowModel: createPaginatedRowModel(),
|
|
88
|
-
sortedRowModel: createSortedRowModel(sortFns),
|
|
89
|
-
filteredRowModel: createFilteredRowModel(filterFns),
|
|
90
|
-
}
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
**Skipping a feature** in `features` means its state slice does not exist on `table.atoms`,
|
|
94
|
-
its options on `createTable` do nothing, and its derived APIs (`table.setSorting`,
|
|
95
|
-
`column.getCanSort`) are not on the instance.
|
|
96
|
-
|
|
97
|
-
## 3. Type your data and define columns
|
|
98
|
-
|
|
99
|
-
```ts
|
|
100
|
-
type Person = {
|
|
101
|
-
firstName: string
|
|
102
|
-
lastName: string
|
|
103
|
-
age: number
|
|
104
|
-
visits: number
|
|
105
|
-
status: 'relationship' | 'complicated' | 'single'
|
|
106
|
-
progress: number
|
|
107
|
-
}
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
Both `ColumnDef` and the column helper take the two generics `<typeof features, TData>`:
|
|
111
|
-
|
|
112
|
-
```ts
|
|
113
|
-
import { createColumnHelper, type ColumnDef } from '@tanstack/svelte-table'
|
|
114
|
-
|
|
115
|
-
const columnHelper = createColumnHelper<typeof features, Person>()
|
|
116
|
-
|
|
117
|
-
const columns = columnHelper.columns([
|
|
118
|
-
columnHelper.accessor('firstName', {
|
|
119
|
-
header: 'First Name',
|
|
120
|
-
cell: (info) => info.getValue(),
|
|
121
|
-
}),
|
|
122
|
-
columnHelper.accessor((row) => row.lastName, {
|
|
123
|
-
id: 'lastName',
|
|
124
|
-
header: () => 'Last Name',
|
|
125
|
-
cell: (info) => info.getValue(),
|
|
126
|
-
}),
|
|
127
|
-
columnHelper.accessor('age', { header: 'Age' }),
|
|
128
|
-
columnHelper.accessor('visits', { header: 'Visits' }),
|
|
129
|
-
columnHelper.accessor('status', { header: 'Status' }),
|
|
130
|
-
columnHelper.accessor('progress', { header: 'Profile Progress' }),
|
|
131
|
-
])
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
Or use raw `ColumnDef` arrays if you don't want the helper:
|
|
135
|
-
|
|
136
|
-
```ts
|
|
137
|
-
const columns: Array<ColumnDef<typeof features, Person>> = [
|
|
138
|
-
{
|
|
139
|
-
accessorKey: 'firstName',
|
|
140
|
-
header: 'First Name',
|
|
141
|
-
cell: (info) => info.getValue(),
|
|
142
|
-
},
|
|
143
|
-
]
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
## 4. Create the table
|
|
147
|
-
|
|
148
|
-
Use Svelte 5 `$state` for the data and pass it through a **reactive getter** so the table
|
|
149
|
-
re-evaluates `data` when the rune changes. The same pattern applies for any other reactive
|
|
150
|
-
option (`columns`, `rowCount`, `state.*`).
|
|
151
|
-
|
|
152
|
-
```svelte
|
|
153
|
-
<script lang="ts">
|
|
154
|
-
import { createTable, tableFeatures } from '@tanstack/svelte-table'
|
|
155
|
-
import { makeData, type Person } from './makeData'
|
|
156
|
-
|
|
157
|
-
const features = tableFeatures({})
|
|
158
|
-
|
|
159
|
-
// ... columns from step 3 ...
|
|
160
|
-
|
|
161
|
-
let data = $state<Person[]>(makeData(20))
|
|
162
|
-
|
|
163
|
-
const refreshData = () => {
|
|
164
|
-
data = makeData(20)
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const table = createTable({
|
|
168
|
-
features,
|
|
169
|
-
rowModels: {},
|
|
170
|
-
columns,
|
|
171
|
-
get data() {
|
|
172
|
-
return data
|
|
173
|
-
},
|
|
174
|
-
})
|
|
175
|
-
</script>
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
`createTable` syncs options inside `$effect.pre`, so external `$state` updates flow into the
|
|
179
|
-
table **before** the DOM reads `getRowModel()` — no stale-frame bugs.
|
|
180
|
-
|
|
181
|
-
## 5. Render with `FlexRender`
|
|
182
|
-
|
|
183
|
-
`FlexRender` handles plain strings, function renderers, component renderers
|
|
184
|
-
(`renderComponent`), and snippet renderers (`renderSnippet`).
|
|
185
|
-
|
|
186
|
-
```svelte
|
|
187
|
-
<script lang="ts">
|
|
188
|
-
import { FlexRender } from '@tanstack/svelte-table'
|
|
189
|
-
</script>
|
|
190
|
-
|
|
191
|
-
<button onclick={refreshData}>Regenerate</button>
|
|
192
|
-
|
|
193
|
-
<table>
|
|
194
|
-
<thead>
|
|
195
|
-
{#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
|
|
196
|
-
<tr>
|
|
197
|
-
{#each headerGroup.headers as header (header.id)}
|
|
198
|
-
<th>
|
|
199
|
-
{#if !header.isPlaceholder}
|
|
200
|
-
<FlexRender {header} />
|
|
201
|
-
{/if}
|
|
202
|
-
</th>
|
|
203
|
-
{/each}
|
|
204
|
-
</tr>
|
|
205
|
-
{/each}
|
|
206
|
-
</thead>
|
|
207
|
-
<tbody>
|
|
208
|
-
{#each table.getRowModel().rows as row (row.id)}
|
|
209
|
-
<tr>
|
|
210
|
-
{#each row.getAllCells() as cell (cell.id)}
|
|
211
|
-
<td><FlexRender {cell} /></td>
|
|
212
|
-
{/each}
|
|
213
|
-
</tr>
|
|
214
|
-
{/each}
|
|
215
|
-
</tbody>
|
|
216
|
-
</table>
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
**Key the `{#each}` blocks on stable ids.** Without keys, Svelte recreates nodes and loses
|
|
220
|
-
focus, scroll, and any per-row component state.
|
|
221
|
-
|
|
222
|
-
## 6. Adding a feature — pagination
|
|
223
|
-
|
|
224
|
-
```svelte
|
|
225
|
-
<script lang="ts">
|
|
226
|
-
import {
|
|
227
|
-
createPaginatedRowModel,
|
|
228
|
-
createTable,
|
|
229
|
-
rowPaginationFeature,
|
|
230
|
-
tableFeatures,
|
|
231
|
-
} from '@tanstack/svelte-table'
|
|
232
|
-
|
|
233
|
-
const features = tableFeatures({ rowPaginationFeature })
|
|
234
|
-
|
|
235
|
-
const table = createTable({
|
|
236
|
-
features,
|
|
237
|
-
rowModels: {
|
|
238
|
-
paginatedRowModel: createPaginatedRowModel(),
|
|
239
|
-
},
|
|
240
|
-
columns,
|
|
241
|
-
get data() {
|
|
242
|
-
return data
|
|
243
|
-
},
|
|
244
|
-
initialState: {
|
|
245
|
-
pagination: { pageIndex: 0, pageSize: 10 },
|
|
246
|
-
},
|
|
247
|
-
})
|
|
248
|
-
</script>
|
|
249
|
-
|
|
250
|
-
<div>
|
|
251
|
-
<button
|
|
252
|
-
onclick={() => table.previousPage()}
|
|
253
|
-
disabled={!table.getCanPreviousPage()}>Prev</button
|
|
254
|
-
>
|
|
255
|
-
<span
|
|
256
|
-
>Page {table.atoms.pagination.get().pageIndex + 1} of {table.getPageCount()}</span
|
|
257
|
-
>
|
|
258
|
-
<button onclick={() => table.nextPage()} disabled={!table.getCanNextPage()}
|
|
259
|
-
>Next</button
|
|
260
|
-
>
|
|
261
|
-
</div>
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
To make pagination reactive in the controls, either pass a selector to `createTable` or use
|
|
265
|
-
`subscribeTable`:
|
|
266
|
-
|
|
267
|
-
```ts
|
|
268
|
-
import { subscribeTable } from '@tanstack/svelte-table'
|
|
269
|
-
|
|
270
|
-
const pagination = subscribeTable(table.atoms.pagination)
|
|
271
|
-
// pagination.current.pageIndex is reactive
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
## 7. `createTableHook` (when you have more than one table)
|
|
275
|
-
|
|
276
|
-
For apps with multiple tables, define the `features`, `rowModels`, and shared components
|
|
277
|
-
once:
|
|
278
|
-
|
|
279
|
-
```ts
|
|
280
|
-
// hooks/table.ts
|
|
281
|
-
import {
|
|
282
|
-
createPaginatedRowModel,
|
|
283
|
-
createSortedRowModel,
|
|
284
|
-
createTableHook,
|
|
285
|
-
rowPaginationFeature,
|
|
286
|
-
rowSortingFeature,
|
|
287
|
-
sortFns,
|
|
288
|
-
tableFeatures,
|
|
289
|
-
} from '@tanstack/svelte-table'
|
|
290
|
-
|
|
291
|
-
export const { createAppTable, createAppColumnHelper } = createTableHook({
|
|
292
|
-
features: tableFeatures({ rowPaginationFeature, rowSortingFeature }),
|
|
293
|
-
rowModels: {
|
|
294
|
-
paginatedRowModel: createPaginatedRowModel(),
|
|
295
|
-
sortedRowModel: createSortedRowModel(sortFns),
|
|
296
|
-
},
|
|
297
|
-
})
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
```svelte
|
|
301
|
-
<script lang="ts">
|
|
302
|
-
import { createAppColumnHelper, createAppTable } from './hooks/table'
|
|
303
|
-
|
|
304
|
-
const columnHelper = createAppColumnHelper<Person>()
|
|
305
|
-
const columns = columnHelper.columns([
|
|
306
|
-
columnHelper.accessor('firstName', { header: 'First' }),
|
|
307
|
-
])
|
|
308
|
-
|
|
309
|
-
const table = createAppTable({
|
|
310
|
-
columns,
|
|
311
|
-
get data() {
|
|
312
|
-
return data
|
|
313
|
-
},
|
|
314
|
-
})
|
|
315
|
-
</script>
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
## Common failure modes
|
|
319
|
-
|
|
320
|
-
- **Svelte 3/4.** Adapter will not work. See top of file.
|
|
321
|
-
- **`createSvelteTable` / `useSvelteTable` / `getCoreRowModel`** — all v8 names. v9 uses
|
|
322
|
-
`createTable` and `rowModels: { paginatedRowModel: createPaginatedRowModel(), ... }`.
|
|
323
|
-
- **Plain `data` instead of `get data()` getter.** Table will not see data updates. Always
|
|
324
|
-
pass a reactive getter for state that lives in `$state`.
|
|
325
|
-
- **Missing feature in `features`.** `table.setSorting` / `column.getCanSort` won't exist.
|
|
326
|
-
TypeScript will tell you; if it doesn't, you're missing the `<typeof features, TData>`
|
|
327
|
-
generics on the column helper or `ColumnDef`.
|
|
328
|
-
- **Plain object instead of `tableFeatures({...})`.** Loses typed atom keys; you'll get
|
|
329
|
-
`unknown` state shapes everywhere.
|
|
330
|
-
- **Unkeyed `{#each}` blocks.** Reuse bugs (focus jumps, wrong row selected).
|
|
331
|
-
- **Reimplementing built-ins.** If you write a manual sort comparator across rows, you're
|
|
332
|
-
re-doing `rowSortingFeature`. Register it instead.
|
|
333
|
-
|
|
334
|
-
## Next steps
|
|
335
|
-
|
|
336
|
-
- `tanstack-table/svelte/table-state` — reactivity model, selectors, subscribeTable, ownership.
|
|
337
|
-
- `tanstack-table/core/filtering` / `pagination` / `sorting` / `row-selection` — feature-by-feature.
|
|
338
|
-
- `tanstack-table/svelte/compose-with-tanstack-query` — server-side data.
|
|
339
|
-
- `tanstack-table/svelte/compose-with-tanstack-virtual` — large datasets.
|
|
340
|
-
- `tanstack-table/svelte/production-readiness` — selector tuning, bundle size.
|