@tanstack/svelte-table 9.0.0-beta.7 → 9.0.0-beta.71
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,256 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: svelte/migrate-v8-to-v9
|
|
3
|
-
description: >
|
|
4
|
-
Mechanical migration from `@tanstack/svelte-table@8` to `@tanstack/svelte-table@9`. v9 in Svelte
|
|
5
|
-
is a full rewrite — Svelte 5 runes only (no Svelte 3/4), no `/legacy` adapter (unlike React),
|
|
6
|
-
`createSvelteTable` → `createTable`, `getCoreRowModel` / `getSortedRowModel` factories → required
|
|
7
|
-
`features` + `rowModels` registration, `flexRender` helper → `<FlexRender>` component,
|
|
8
|
-
writable-store `state` → rune-based getters / external atoms, `onStateChange` → per-slice
|
|
9
|
-
`on[State]Change` or `atoms`. Plan a feature-by-feature audit, not a search-and-replace.
|
|
10
|
-
type: lifecycle
|
|
11
|
-
library: tanstack-table
|
|
12
|
-
framework: svelte
|
|
13
|
-
library_version: '9.0.0-alpha.48'
|
|
14
|
-
requires:
|
|
15
|
-
- setup
|
|
16
|
-
- state-management
|
|
17
|
-
- column-definitions
|
|
18
|
-
sources:
|
|
19
|
-
- TanStack/table:docs/framework/svelte/svelte-table.md
|
|
20
|
-
- TanStack/table:docs/framework/svelte/guide/table-state.md
|
|
21
|
-
- TanStack/table:packages/svelte-table/src/
|
|
22
|
-
- TanStack/table:examples/svelte/basic-create-table/
|
|
23
|
-
- TanStack/table:examples/svelte/basic-external-atoms/
|
|
24
|
-
- TanStack/table:examples/svelte/basic-external-state/
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
# Migrate v8 → v9 (Svelte)
|
|
28
|
-
|
|
29
|
-
## CRITICAL: v9 = Svelte 5
|
|
30
|
-
|
|
31
|
-
**v9 of the Svelte adapter only supports Svelte 5+.** No backport. No shim. No `/legacy` import.
|
|
32
|
-
The v9 adapter is built on Svelte 5 runes (`$state`, `$derived.by`, `$effect.pre`). If your app
|
|
33
|
-
is still on Svelte 3/4, you have two real options:
|
|
34
|
-
|
|
35
|
-
1. Stay on `@tanstack/svelte-table@8`. v8 keeps working with the Svelte 3/4 writable-store API.
|
|
36
|
-
2. Migrate the app to Svelte 5 first, then migrate the table.
|
|
37
|
-
|
|
38
|
-
There is no third option. Trying to install v9 on a Svelte 4 codebase will error at compile.
|
|
39
|
-
|
|
40
|
-
> This makes the Svelte migration heavier than React's. React has a `/legacy` re-export that
|
|
41
|
-
> mirrors the v8 surface; Svelte does not. Plan for a real rewrite of every table screen.
|
|
42
|
-
|
|
43
|
-
## What changed at the type / API level
|
|
44
|
-
|
|
45
|
-
| v8 | v9 |
|
|
46
|
-
| -------------------------------------------------- | ------------------------------------------------------------------------------------- |
|
|
47
|
-
| `createSvelteTable(options)` | `createTable(options, selector?)` |
|
|
48
|
-
| `getCoreRowModel()` | included by default; no factory |
|
|
49
|
-
| `getPaginationRowModel()` | `rowModels.paginatedRowModel: createPaginatedRowModel()` |
|
|
50
|
-
| `getSortedRowModel()` | `rowModels.sortedRowModel: createSortedRowModel(sortFns)` |
|
|
51
|
-
| `getFilteredRowModel()` | `rowModels.filteredRowModel: createFilteredRowModel(filterFns)` |
|
|
52
|
-
| `getExpandedRowModel()` | `rowModels.expandedRowModel: createExpandedRowModel()` |
|
|
53
|
-
| `getGroupedRowModel()` | `rowModels.groupedRowModel: createGroupedRowModel(aggregationFns)` |
|
|
54
|
-
| `getFacetedRowModel()` / `MinMax` / `UniqueValues` | facet APIs auto-derived when `*Facet*Feature` registered |
|
|
55
|
-
| `flexRender(template, ctx)` helper | `<FlexRender header={h} />` / `<FlexRender cell={c} />` / `<FlexRender footer={h} />` |
|
|
56
|
-
| `ColumnDef<TData>` | `ColumnDef<TFeatures, TData>` (extra generic) |
|
|
57
|
-
| `createColumnHelper<TData>()` | `createColumnHelper<TFeatures, TData>()` |
|
|
58
|
-
| writable store on the table instance | `table.atoms.<slice>` + `table.store` + `table.state` |
|
|
59
|
-
| `onStateChange` (monolithic) | per-slice `on[State]Change`, or external `atoms` |
|
|
60
|
-
| `useSvelteTable` (rare) | gone |
|
|
61
|
-
|
|
62
|
-
## What did NOT change
|
|
63
|
-
|
|
64
|
-
- The data array is still the source of truth; columns are still defined the same way
|
|
65
|
-
shape-wise (`accessorKey` / `accessorFn` / `header` / `cell` / `footer`).
|
|
66
|
-
- Filter, sort, aggregation function registries are still `filterFns`, `sortFns`,
|
|
67
|
-
`aggregationFns` — but now passed into row-model factories instead of being auto-resolved.
|
|
68
|
-
- Feature APIs (`table.nextPage()`, `column.getCanSort()`, `row.toggleSelected()`) keep the
|
|
69
|
-
same names.
|
|
70
|
-
|
|
71
|
-
## Migration checklist (per file)
|
|
72
|
-
|
|
73
|
-
For each Svelte component that uses the table:
|
|
74
|
-
|
|
75
|
-
1. **Upgrade Svelte.** Ensure the app is on Svelte 5 and the component compiles in runes mode.
|
|
76
|
-
2. **Replace store with rune.** `let data = writable([])` → `let data = $state([])`. Reads
|
|
77
|
-
inside markup are no longer `$data` — just `data`.
|
|
78
|
-
3. **Import surface.** `import { createSvelteTable, getCoreRowModel, flexRender }` →
|
|
79
|
-
`import { createTable, FlexRender, tableFeatures, ... }`.
|
|
80
|
-
4. **Add `features`.** Create `const features = tableFeatures({ ... only the features this
|
|
81
|
-
table uses ... })`.
|
|
82
|
-
5. **Move row-model factories.** Every `get*RowModel: get*RowModel()` becomes a `rowModels`
|
|
83
|
-
entry with the matching `create*RowModel(*Fns)` factory.
|
|
84
|
-
6. **Generic columns.** Add `<typeof features, TData>` to `ColumnDef<>` and
|
|
85
|
-
`createColumnHelper<>()`. TypeScript will tell you when you missed one.
|
|
86
|
-
7. **`data` as a getter.** `data: data` → `get data() { return data }`. Same for any other
|
|
87
|
-
reactive option (`state.*`, `columns`, `rowCount`).
|
|
88
|
-
8. **State.** Pick the new ownership model — see below.
|
|
89
|
-
9. **Rendering.** `{flexRender(header.column.columnDef.header, header.getContext())}` →
|
|
90
|
-
`<FlexRender header={header} />`. Same for cells and footers.
|
|
91
|
-
10. **Components / snippets in cells.** v8: pass a Svelte component constructor. v9: wrap with
|
|
92
|
-
`renderComponent(MyCell, props)` or `renderSnippet(snippet, args)`.
|
|
93
|
-
|
|
94
|
-
## State ownership — choose one per slice
|
|
95
|
-
|
|
96
|
-
### Was: `writable` store + `onStateChange`
|
|
97
|
-
|
|
98
|
-
```svelte
|
|
99
|
-
<script lang="ts">
|
|
100
|
-
// v8
|
|
101
|
-
import { writable } from 'svelte/store'
|
|
102
|
-
const options = writable({ ... })
|
|
103
|
-
$: $options.state = $state
|
|
104
|
-
</script>
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Now: pick one of three
|
|
108
|
-
|
|
109
|
-
**Internal (default).** Pass only `initialState` and let the table own it.
|
|
110
|
-
|
|
111
|
-
```ts
|
|
112
|
-
const table = createTable({
|
|
113
|
-
features,
|
|
114
|
-
rowModels: { paginatedRowModel: createPaginatedRowModel() },
|
|
115
|
-
columns,
|
|
116
|
-
get data() {
|
|
117
|
-
return data
|
|
118
|
-
},
|
|
119
|
-
initialState: {
|
|
120
|
-
pagination: { pageIndex: 0, pageSize: 25 },
|
|
121
|
-
},
|
|
122
|
-
})
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
**External `state` + per-slice `on[State]Change`.** Closest to a literal v8 port.
|
|
126
|
-
|
|
127
|
-
```svelte
|
|
128
|
-
<script lang="ts">
|
|
129
|
-
let sorting: SortingState = $state([])
|
|
130
|
-
let pagination: PaginationState = $state({ pageIndex: 0, pageSize: 10 })
|
|
131
|
-
|
|
132
|
-
const table = createTable({
|
|
133
|
-
features,
|
|
134
|
-
rowModels: { ... },
|
|
135
|
-
columns,
|
|
136
|
-
get data() { return data },
|
|
137
|
-
state: {
|
|
138
|
-
get sorting() { return sorting },
|
|
139
|
-
get pagination() { return pagination },
|
|
140
|
-
},
|
|
141
|
-
onSortingChange: (updater) => {
|
|
142
|
-
sorting = updater instanceof Function ? updater(sorting) : updater
|
|
143
|
-
},
|
|
144
|
-
onPaginationChange: (updater) => {
|
|
145
|
-
pagination = updater instanceof Function ? updater(pagination) : updater
|
|
146
|
-
},
|
|
147
|
-
})
|
|
148
|
-
</script>
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
**External atoms (preferred for shared state).** Atomic, subscribable from anywhere.
|
|
152
|
-
|
|
153
|
-
```ts
|
|
154
|
-
import { createAtom, useSelector } from '@tanstack/svelte-store'
|
|
155
|
-
|
|
156
|
-
const sortingAtom = createAtom<SortingState>([])
|
|
157
|
-
const paginationAtom = createAtom<PaginationState>({ pageIndex: 0, pageSize: 10 })
|
|
158
|
-
|
|
159
|
-
const sorting = useSelector(sortingAtom)
|
|
160
|
-
const pagination = useSelector(paginationAtom)
|
|
161
|
-
|
|
162
|
-
const table = createTable({
|
|
163
|
-
features,
|
|
164
|
-
rowModels: { ... },
|
|
165
|
-
columns,
|
|
166
|
-
get data() { return data },
|
|
167
|
-
atoms: {
|
|
168
|
-
sorting: sortingAtom,
|
|
169
|
-
pagination: paginationAtom,
|
|
170
|
-
},
|
|
171
|
-
})
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
**Do not combine** `state.pagination` with `atoms.pagination` — atoms always win, `state` is
|
|
175
|
-
discarded silently, you'll think your callbacks aren't firing.
|
|
176
|
-
|
|
177
|
-
## Rendering migration cheat sheet
|
|
178
|
-
|
|
179
|
-
```svelte
|
|
180
|
-
<!-- v8 -->
|
|
181
|
-
<th>
|
|
182
|
-
{#if !header.isPlaceholder}
|
|
183
|
-
<svelte:component
|
|
184
|
-
this={flexRender(header.column.columnDef.header, header.getContext())}
|
|
185
|
-
/>
|
|
186
|
-
{/if}
|
|
187
|
-
</th>
|
|
188
|
-
|
|
189
|
-
<!-- v9 -->
|
|
190
|
-
<th>
|
|
191
|
-
{#if !header.isPlaceholder}
|
|
192
|
-
<FlexRender {header} />
|
|
193
|
-
{/if}
|
|
194
|
-
</th>
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
For cells that render a custom Svelte component:
|
|
198
|
-
|
|
199
|
-
```svelte
|
|
200
|
-
<!-- v8: column def -->
|
|
201
|
-
{ cell: () => MyCellComponent }
|
|
202
|
-
|
|
203
|
-
<!-- v9: column def -->
|
|
204
|
-
import { renderComponent } from '@tanstack/svelte-table'
|
|
205
|
-
{ cell: ({ row }) => renderComponent(MyCellComponent, { row }) }
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
For inline snippets:
|
|
209
|
-
|
|
210
|
-
```svelte
|
|
211
|
-
<!-- v9 -->
|
|
212
|
-
import { renderSnippet } from '@tanstack/svelte-table'
|
|
213
|
-
|
|
214
|
-
{#snippet myCell(row)}
|
|
215
|
-
<strong>{row.original.firstName}</strong>
|
|
216
|
-
{/snippet}
|
|
217
|
-
|
|
218
|
-
{ cell: ({ row }) => renderSnippet(myCell, row) }
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
## After the rewrite — verify
|
|
222
|
-
|
|
223
|
-
- `pnpm test:types` (or `svelte-check`) catches missing `<typeof features, TData>` generics,
|
|
224
|
-
missing `features` / `rowModels`, and feature APIs called on tables that didn't register
|
|
225
|
-
them.
|
|
226
|
-
- `pnpm build` should pass with the new `features` set. Bundle should shrink — only the
|
|
227
|
-
features you register are included.
|
|
228
|
-
- Click through every table screen. Reset buttons, multi-sort, pagination reset on filter,
|
|
229
|
-
expanded-row count under pagination — these are all the spots where v8 muscle memory
|
|
230
|
-
reaches for an option (`autoResetPageIndex` etc.) that's still named the same in v9 but
|
|
231
|
-
only takes effect if the matching feature is registered.
|
|
232
|
-
|
|
233
|
-
## Common failure modes during migration
|
|
234
|
-
|
|
235
|
-
- **Trying to skip the Svelte 5 upgrade.** Will not work.
|
|
236
|
-
- **Reaching for `useLegacyTable`.** Doesn't exist in `@tanstack/svelte-table`. That's a
|
|
237
|
-
React-only escape hatch.
|
|
238
|
-
- **Importing from `@tanstack/table-core` directly.** Re-exported by the adapter — go through
|
|
239
|
-
the adapter.
|
|
240
|
-
- **Plain `data` instead of `get data()` getter.** Largest single source of "but my data
|
|
241
|
-
changed" bugs during migration.
|
|
242
|
-
- **Plain `features: { rowPaginationFeature }` instead of `tableFeatures({...})`.** Loses
|
|
243
|
-
inference; you'll get `any` everywhere.
|
|
244
|
-
- **Forgetting feature registration after copying the row-model factory.** Adding
|
|
245
|
-
`paginatedRowModel: createPaginatedRowModel()` without `rowPaginationFeature` in
|
|
246
|
-
`features` does nothing.
|
|
247
|
-
- **Reimplementing v8 helpers** (`flexRender`-style functions, manual store subscriptions,
|
|
248
|
-
hand-rolled selectors) instead of using `FlexRender` / `subscribeTable` / atom selectors.
|
|
249
|
-
That's the #1 AI tell on this migration.
|
|
250
|
-
|
|
251
|
-
## Related skills
|
|
252
|
-
|
|
253
|
-
- `tanstack-table/svelte/getting-started` — clean-slate setup.
|
|
254
|
-
- `tanstack-table/svelte/table-state` — the v9 state model in depth.
|
|
255
|
-
- `tanstack-table/core/state-management` — atom precedence rules.
|
|
256
|
-
- `tanstack-table/svelte/production-readiness` — post-migration tuning.
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: svelte/production-readiness
|
|
3
|
-
description: >
|
|
4
|
-
Ship-ready optimizations for `@tanstack/svelte-table@9` on Svelte 5. Tree-shake by registering
|
|
5
|
-
ONLY the `features` you use; keep `features`, `columns`, and `data` stable; replace broad
|
|
6
|
-
`(state) => state` selectors with narrow projections on `createTable`; reach for
|
|
7
|
-
`subscribeTable(atom, selector?)` when only one block of markup should react; lean on rune-aware
|
|
8
|
-
atom reads (`table.atoms.<slice>.get()`) for non-reactive paths; key every `{#each}` block on
|
|
9
|
-
stable ids; debounce high-frequency writes with `@tanstack/svelte-pacer`. Svelte 5+ only.
|
|
10
|
-
type: lifecycle
|
|
11
|
-
library: tanstack-table
|
|
12
|
-
framework: svelte
|
|
13
|
-
library_version: '9.0.0-alpha.48'
|
|
14
|
-
requires:
|
|
15
|
-
- setup
|
|
16
|
-
- state-management
|
|
17
|
-
- svelte/table-state
|
|
18
|
-
sources:
|
|
19
|
-
- TanStack/table:docs/guide/features.md
|
|
20
|
-
- TanStack/table:docs/framework/svelte/guide/table-state.md
|
|
21
|
-
- TanStack/table:packages/svelte-table/src/createTable.svelte.ts
|
|
22
|
-
- TanStack/table:packages/svelte-table/src/subscribe.ts
|
|
23
|
-
- TanStack/table:examples/svelte/basic-external-atoms/
|
|
24
|
-
- TanStack/table:examples/svelte/virtualized-rows/
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
# Production Readiness — Svelte
|
|
28
|
-
|
|
29
|
-
Once your tables work, this is the checklist for making them fast and small. Most of these are
|
|
30
|
-
v9-specific — v8 tables won't have any of these levers.
|
|
31
|
-
|
|
32
|
-
## 1. Register only the features you use
|
|
33
|
-
|
|
34
|
-
`features` is the bundle gate. Any feature you don't register is tree-shaken out — including
|
|
35
|
-
its state slice, its API surface, and its reactive plumbing.
|
|
36
|
-
|
|
37
|
-
```ts
|
|
38
|
-
// good — minimal table, ~smallest bundle
|
|
39
|
-
const features = tableFeatures({})
|
|
40
|
-
|
|
41
|
-
// good — feature-by-feature opt-in
|
|
42
|
-
const features = tableFeatures({
|
|
43
|
-
rowPaginationFeature,
|
|
44
|
-
rowSortingFeature,
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
// bad — kitchen sink, every state slice created even when unused
|
|
48
|
-
const features = tableFeatures({
|
|
49
|
-
columnFilteringFeature,
|
|
50
|
-
columnGroupingFeature,
|
|
51
|
-
columnOrderingFeature,
|
|
52
|
-
columnPinningFeature,
|
|
53
|
-
columnResizingFeature,
|
|
54
|
-
columnSizingFeature,
|
|
55
|
-
columnVisibilityFeature,
|
|
56
|
-
globalFilteringFeature,
|
|
57
|
-
rowExpandingFeature,
|
|
58
|
-
rowPaginationFeature,
|
|
59
|
-
rowPinningFeature,
|
|
60
|
-
rowSelectionFeature,
|
|
61
|
-
rowSortingFeature,
|
|
62
|
-
})
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
If you find yourself running a table without a feature's UI ever showing, drop the feature.
|
|
66
|
-
|
|
67
|
-
## 2. Stable identities for `features`, `columns`, `data`
|
|
68
|
-
|
|
69
|
-
`createTable` syncs options in `$effect.pre`. If any of these identities flip every component
|
|
70
|
-
run, the table re-syncs more than it needs to.
|
|
71
|
-
|
|
72
|
-
- **`features` and `rowModels`**: declare at module scope, not inside the component
|
|
73
|
-
function, and never inside `$derived` / `$effect`.
|
|
74
|
-
- **`columns`**: same — module scope or `$state.frozen` / a non-reactive `const` in the
|
|
75
|
-
component. Reactive recompute of columns is rare and almost always a bug.
|
|
76
|
-
- **`data`**: pass with a getter (`get data()`) so the reference is stable when the data
|
|
77
|
-
doesn't change. If you're reshaping data inside the component, do it once in a `$derived`,
|
|
78
|
-
not on every read.
|
|
79
|
-
|
|
80
|
-
```svelte
|
|
81
|
-
<script lang="ts">
|
|
82
|
-
// module scope is fine in .svelte too, when truly static
|
|
83
|
-
const features = tableFeatures({ rowPaginationFeature })
|
|
84
|
-
const rowModels = { paginatedRowModel: createPaginatedRowModel() }
|
|
85
|
-
const columns = columnHelper.columns([
|
|
86
|
-
/* ... */
|
|
87
|
-
])
|
|
88
|
-
|
|
89
|
-
let rawRows = $state<Person[]>([])
|
|
90
|
-
const data = $derived(rawRows.map(normalize)) // computed once per rawRows change
|
|
91
|
-
|
|
92
|
-
const table = createTable({
|
|
93
|
-
features,
|
|
94
|
-
rowModels,
|
|
95
|
-
columns,
|
|
96
|
-
get data() {
|
|
97
|
-
return data
|
|
98
|
-
},
|
|
99
|
-
})
|
|
100
|
-
</script>
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
## 3. Narrow the `table.state` selector
|
|
104
|
-
|
|
105
|
-
The default selector is `(state) => state`. That makes `table.state` re-run any consumer when
|
|
106
|
-
**any** slice changes. Pass a focused selector and you only re-render markup that actually
|
|
107
|
-
depends on that slice.
|
|
108
|
-
|
|
109
|
-
```ts
|
|
110
|
-
// good — only re-projects when pagination changes
|
|
111
|
-
const table = createTable(options, (state) => ({
|
|
112
|
-
pagination: state.pagination,
|
|
113
|
-
}))
|
|
114
|
-
|
|
115
|
-
// even better — only what your UI actually reads
|
|
116
|
-
const table = createTable(options, (state) => ({
|
|
117
|
-
pageIndex: state.pagination.pageIndex,
|
|
118
|
-
pageSize: state.pagination.pageSize,
|
|
119
|
-
}))
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
If different parts of the UI need different slices, **don't** widen the selector — use
|
|
123
|
-
`subscribeTable` instead (next section).
|
|
124
|
-
|
|
125
|
-
## 4. Reach for `subscribeTable` for fine-grained reactivity
|
|
126
|
-
|
|
127
|
-
`subscribeTable(source, selector?)` is the per-block subscription. It returns an object whose
|
|
128
|
-
`.current` re-runs only when the selected value changes (shallow compared).
|
|
129
|
-
|
|
130
|
-
```svelte
|
|
131
|
-
<script lang="ts">
|
|
132
|
-
import { subscribeTable } from '@tanstack/svelte-table'
|
|
133
|
-
|
|
134
|
-
// dedicated subscription for the pager
|
|
135
|
-
const pagination = subscribeTable(table.atoms.pagination)
|
|
136
|
-
|
|
137
|
-
// dedicated subscription for a single row's selection state
|
|
138
|
-
const isSelected = subscribeTable(
|
|
139
|
-
table.atoms.rowSelection,
|
|
140
|
-
(rs) => !!rs[row.id],
|
|
141
|
-
)
|
|
142
|
-
</script>
|
|
143
|
-
|
|
144
|
-
<input type="checkbox" checked={isSelected.current} />
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
Use it inside per-row components — the row block re-renders only on its own selection toggle,
|
|
148
|
-
not on every row's toggle.
|
|
149
|
-
|
|
150
|
-
## 5. Non-reactive reads where you don't need reactivity
|
|
151
|
-
|
|
152
|
-
Inside event handlers, derived calculations, or one-shot logic, read atoms directly. Cheaper
|
|
153
|
-
than subscribing.
|
|
154
|
-
|
|
155
|
-
```ts
|
|
156
|
-
function exportSelected() {
|
|
157
|
-
const selection = table.atoms.rowSelection.get()
|
|
158
|
-
const selectedIds = Object.keys(selection).filter((id) => selection[id])
|
|
159
|
-
// ...
|
|
160
|
-
}
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
`table.state` is the same idea for a full snapshot.
|
|
164
|
-
|
|
165
|
-
## 6. Key every `{#each}` block on a stable id
|
|
166
|
-
|
|
167
|
-
Svelte without keys recreates nodes on reorder. Result: lost input focus, lost scroll, lost
|
|
168
|
-
component state. Every TanStack Table loop has a stable id.
|
|
169
|
-
|
|
170
|
-
```svelte
|
|
171
|
-
{#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
|
|
172
|
-
<tr>
|
|
173
|
-
{#each headerGroup.headers as header (header.id)}
|
|
174
|
-
<th>...</th>
|
|
175
|
-
{/each}
|
|
176
|
-
</tr>
|
|
177
|
-
{/each}
|
|
178
|
-
|
|
179
|
-
{#each table.getRowModel().rows as row (row.id)}
|
|
180
|
-
<tr>
|
|
181
|
-
{#each row.getVisibleCells() as cell (cell.id)}
|
|
182
|
-
<td>...</td>
|
|
183
|
-
{/each}
|
|
184
|
-
</tr>
|
|
185
|
-
{/each}
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
## 7. Don't fight `$effect.pre`
|
|
189
|
-
|
|
190
|
-
`createTable` is already syncing options in `$effect.pre`. Don't write a second `$effect`
|
|
191
|
-
that calls `table.setOptions` — it'll race with the built-in sync and may render with stale
|
|
192
|
-
state.
|
|
193
|
-
|
|
194
|
-
If you need to react to an option change with a side effect, put the side effect in your own
|
|
195
|
-
`$effect`, not the option write.
|
|
196
|
-
|
|
197
|
-
## 8. Debounce high-frequency writes
|
|
198
|
-
|
|
199
|
-
Two places will hammer table state at keystroke / pointermove rate:
|
|
200
|
-
|
|
201
|
-
- **Filter inputs.** Wrap `setFilterValue` calls with a debounced callback.
|
|
202
|
-
- **Column resizing.** v9 commits `columnSizing` continuously by default; use the resize-end
|
|
203
|
-
commit mode or debounce.
|
|
204
|
-
|
|
205
|
-
```ts
|
|
206
|
-
import { createDebouncer } from '@tanstack/svelte-pacer/debouncer'
|
|
207
|
-
|
|
208
|
-
const debouncedSetFilter = createDebouncer(
|
|
209
|
-
(value: string) => column.setFilterValue(value),
|
|
210
|
-
{ wait: 200 },
|
|
211
|
-
)
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
See the `compose-with-tanstack-pacer` skill for full examples.
|
|
215
|
-
|
|
216
|
-
## 9. Virtualization for large datasets
|
|
217
|
-
|
|
218
|
-
`getRowModel().rows.length > ~1000` and rows are simple? Performance is fine without
|
|
219
|
-
virtualization. Above that, or with heavy per-row markup, use `@tanstack/svelte-virtual`. See
|
|
220
|
-
the `compose-with-tanstack-virtual` skill.
|
|
221
|
-
|
|
222
|
-
## 10. Don't ship debug flags
|
|
223
|
-
|
|
224
|
-
`debugTable: true`, `debugRows`, `debugHeaders` all log. Strip them or gate on `import.meta.env.DEV`.
|
|
225
|
-
|
|
226
|
-
## 11. Don't reimplement built-ins
|
|
227
|
-
|
|
228
|
-
The #1 production-readiness regression we see in audits: somebody hand-rolled the thing the
|
|
229
|
-
table already does. If you're writing any of these, register the feature instead.
|
|
230
|
-
|
|
231
|
-
- Hand-rolled sort comparator across rows → `rowSortingFeature` + `createSortedRowModel`
|
|
232
|
-
- Hand-rolled page math (`rows.slice(start, end)`) → `rowPaginationFeature` +
|
|
233
|
-
`createPaginatedRowModel`
|
|
234
|
-
- Hand-rolled selection toggle (`selected[row.id] = !selected[row.id]`) → `rowSelectionFeature`
|
|
235
|
-
- Hand-rolled column hide map → `columnVisibilityFeature`
|
|
236
|
-
- Hand-rolled column resizer → `columnResizingFeature`
|
|
237
|
-
- Hand-rolled debounced filter that doesn't update through `setFilterValue` →
|
|
238
|
-
`columnFilteringFeature` + pacer
|
|
239
|
-
|
|
240
|
-
Each rewrite breaks tree-shaking, breaks the reset APIs, and breaks devtools introspection.
|
|
241
|
-
|
|
242
|
-
## Quick smoke test before shipping
|
|
243
|
-
|
|
244
|
-
- Bundle: does the table chunk match the features you registered? (`pnpm build` and inspect.)
|
|
245
|
-
- DevTools profiler: clicking sort triggers exactly one re-render of the headers and rows,
|
|
246
|
-
not every consumer of `table.state`.
|
|
247
|
-
- Resize / filter: no jank, no per-keystroke server hits (pacer / debounce).
|
|
248
|
-
- Reload: state restored from your atom / URL / storage, no flicker.
|
|
249
|
-
- Stress: 100k-row dataset with virtualization stays interactive.
|
|
250
|
-
|
|
251
|
-
## Related skills
|
|
252
|
-
|
|
253
|
-
- `tanstack-table/svelte/table-state` — selectors, atoms, subscribeTable.
|
|
254
|
-
- `tanstack-table/svelte/compose-with-tanstack-pacer` — debounce patterns.
|
|
255
|
-
- `tanstack-table/svelte/compose-with-tanstack-virtual` — virtualization.
|
|
256
|
-
- `tanstack-table/svelte/compose-with-tanstack-store` — atom ownership patterns.
|