@tanstack/vue-table 9.0.0-beta.6 → 9.0.0-beta.62

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.
Files changed (61) hide show
  1. package/README.md +2 -0
  2. package/dist/FlexRender.d.ts +1 -2
  3. package/dist/FlexRender.js +5 -5
  4. package/dist/createTableHook.d.ts +39 -13
  5. package/dist/createTableHook.js +8 -23
  6. package/dist/experimental-worker-plugin.d.ts +1 -0
  7. package/dist/experimental-worker-plugin.js +3 -0
  8. package/dist/index.d.ts +2 -2
  9. package/dist/merge-proxy.js +1 -2
  10. package/dist/reactivity.js +3 -8
  11. package/dist/useTable.d.ts +2 -10
  12. package/dist/useTable.js +4 -9
  13. package/package.json +12 -20
  14. package/skills/create-table-hook/SKILL.md +146 -0
  15. package/skills/getting-started/SKILL.md +144 -0
  16. package/skills/migrate-v8-to-v9/SKILL.md +185 -0
  17. package/skills/table-state/SKILL.md +191 -0
  18. package/skills/with-tanstack-query/SKILL.md +127 -0
  19. package/skills/with-tanstack-virtual/SKILL.md +116 -0
  20. package/dist/FlexRender.cjs +0 -80
  21. package/dist/FlexRender.cjs.map +0 -1
  22. package/dist/FlexRender.d.cts +0 -63
  23. package/dist/FlexRender.js.map +0 -1
  24. package/dist/createTableHook.cjs +0 -194
  25. package/dist/createTableHook.cjs.map +0 -1
  26. package/dist/createTableHook.d.cts +0 -135
  27. package/dist/createTableHook.js.map +0 -1
  28. package/dist/flex-render.cjs +0 -5
  29. package/dist/flex-render.d.cts +0 -2
  30. package/dist/index.cjs +0 -17
  31. package/dist/index.d.cts +0 -5
  32. package/dist/merge-proxy.cjs +0 -77
  33. package/dist/merge-proxy.cjs.map +0 -1
  34. package/dist/merge-proxy.js.map +0 -1
  35. package/dist/reactivity.cjs +0 -64
  36. package/dist/reactivity.cjs.map +0 -1
  37. package/dist/reactivity.js.map +0 -1
  38. package/dist/static-functions.cjs +0 -9
  39. package/dist/static-functions.d.cts +0 -1
  40. package/dist/useTable.cjs +0 -76
  41. package/dist/useTable.cjs.map +0 -1
  42. package/dist/useTable.d.cts +0 -43
  43. package/dist/useTable.js.map +0 -1
  44. package/skills/vue/client-to-server/SKILL.md +0 -365
  45. package/skills/vue/compose-with-tanstack-form/SKILL.md +0 -369
  46. package/skills/vue/compose-with-tanstack-pacer/SKILL.md +0 -318
  47. package/skills/vue/compose-with-tanstack-query/SKILL.md +0 -385
  48. package/skills/vue/compose-with-tanstack-store/SKILL.md +0 -301
  49. package/skills/vue/compose-with-tanstack-virtual/SKILL.md +0 -340
  50. package/skills/vue/getting-started/SKILL.md +0 -409
  51. package/skills/vue/migrate-v8-to-v9/SKILL.md +0 -375
  52. package/skills/vue/production-readiness/SKILL.md +0 -271
  53. package/skills/vue/table-state/SKILL.md +0 -403
  54. package/src/FlexRender.ts +0 -138
  55. package/src/createTableHook.ts +0 -534
  56. package/src/flex-render.ts +0 -1
  57. package/src/index.ts +0 -4
  58. package/src/merge-proxy.ts +0 -132
  59. package/src/reactivity.ts +0 -89
  60. package/src/static-functions.ts +0 -1
  61. package/src/useTable.ts +0 -181
@@ -1,340 +0,0 @@
1
- ---
2
- name: vue/compose-with-tanstack-virtual
3
- description: >
4
- `@tanstack/vue-table` v9 does not include virtualization — pair with `@tanstack/vue-virtual`.
5
- Standard row-virtualization pattern: get the row array from `table.getRowModel().rows`, feed
6
- `rows.length` to `useVirtualizer(computed(() => ({ count, estimateSize, getScrollElement,
7
- overscan })))`, iterate `rowVirtualizer.value.getVirtualItems()` instead of `rows.map`,
8
- absolute-position each row with `transform: translateY(virtualRow.start)px`, and use
9
- `display: grid` on `<table>`/`<thead>`/`<tbody>`. The virtualizer MUST live in the deepest
10
- component possible so unrelated state changes don't re-run it. Column virtualization mirrors
11
- the shape with `horizontal: true` plus padding-left/right placeholder cells. Skip
12
- `measureElement` on Firefox — it returns inconsistent table-row heights.
13
- type: composition
14
- library: tanstack-table
15
- framework: vue
16
- library_version: '9.0.0-alpha.48'
17
- requires:
18
- - vue/table-state
19
- - row-expanding
20
- sources:
21
- - docs/guide/virtualization.md
22
- - examples/vue/virtualized-rows/src/App.vue
23
- - examples/vue/virtualized-columns/
24
- - examples/vue/virtualized-infinite-scrolling/
25
- ---
26
-
27
- # Compose @tanstack/vue-table with @tanstack/vue-virtual
28
-
29
- ## Dependencies
30
-
31
- ```bash
32
- pnpm add @tanstack/vue-table @tanstack/vue-virtual
33
- ```
34
-
35
- `@tanstack/vue-virtual` is the Vue adapter for TanStack Virtual. Its key Vue-isms:
36
-
37
- - `useVirtualizer(optionsRef)` accepts a `ref`/`computed` of options (or a getter), and
38
- returns a `Ref<Virtualizer>` — you read `.value.getVirtualItems()`, `.value.getTotalSize()`, etc.
39
- - `getScrollElement` resolves the scrollable container via a template ref.
40
-
41
- ## Setup — row virtualization, the standard pattern
42
-
43
- ```vue
44
- <script setup lang="ts">
45
- import { computed, ref } from 'vue'
46
- import {
47
- FlexRender,
48
- columnSizingFeature,
49
- createSortedRowModel,
50
- rowSortingFeature,
51
- sortFns,
52
- tableFeatures,
53
- useTable,
54
- type ColumnDef,
55
- } from '@tanstack/vue-table'
56
- import { useVirtualizer } from '@tanstack/vue-virtual'
57
- import { makeData, type Person } from './makeData'
58
-
59
- const features = tableFeatures({ columnSizingFeature, rowSortingFeature })
60
-
61
- const columns: ColumnDef<typeof features, Person>[] = [
62
- { accessorKey: 'firstName' },
63
- { accessorKey: 'lastName' },
64
- { accessorKey: 'age' },
65
- ]
66
-
67
- const data = ref<Person[]>(makeData(50_000))
68
-
69
- const table = useTable({
70
- features,
71
- rowModels: { sortedRowModel: createSortedRowModel(sortFns) },
72
- columns,
73
- data,
74
- })
75
-
76
- const rows = computed(() => table.getRowModel().rows)
77
-
78
- // 1) Template ref to the scrollable container.
79
- const tableContainerRef = ref<HTMLDivElement | null>(null)
80
-
81
- // 2) Reactive virtualizer options — recomputed when rows.length changes.
82
- const rowVirtualizerOptions = computed(() => ({
83
- count: rows.value.length,
84
- estimateSize: () => 33,
85
- getScrollElement: () => tableContainerRef.value,
86
- overscan: 5,
87
- }))
88
-
89
- const rowVirtualizer = useVirtualizer(rowVirtualizerOptions)
90
-
91
- const virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())
92
- const totalSize = computed(() => rowVirtualizer.value.getTotalSize())
93
- </script>
94
-
95
- <template>
96
- <div
97
- ref="tableContainerRef"
98
- :style="{ overflow: 'auto', position: 'relative', height: '800px' }"
99
- >
100
- <!-- display: grid is required for absolute-positioned virtual rows -->
101
- <table :style="{ display: 'grid' }">
102
- <thead
103
- :style="{ display: 'grid', position: 'sticky', top: 0, zIndex: 1 }"
104
- >
105
- <tr
106
- v-for="hg in table.getHeaderGroups()"
107
- :key="hg.id"
108
- :style="{ display: 'flex', width: '100%' }"
109
- >
110
- <th
111
- v-for="h in hg.headers"
112
- :key="h.id"
113
- :style="{ width: `${h.getSize()}px` }"
114
- >
115
- <FlexRender v-if="!h.isPlaceholder" :header="h" />
116
- </th>
117
- </tr>
118
- </thead>
119
- <tbody
120
- :style="{
121
- display: 'grid',
122
- height: `${totalSize}px`,
123
- position: 'relative',
124
- }"
125
- >
126
- <tr
127
- v-for="vRow in virtualRows"
128
- :key="rows[vRow.index].id"
129
- :data-index="vRow.index"
130
- :style="{
131
- display: 'flex',
132
- position: 'absolute',
133
- transform: `translateY(${vRow.start}px)`,
134
- width: '100%',
135
- }"
136
- >
137
- <td
138
- v-for="cell in rows[vRow.index].getAllCells()"
139
- :key="cell.id"
140
- :style="{ display: 'flex', width: `${cell.column.getSize()}px` }"
141
- >
142
- <FlexRender :cell="cell" />
143
- </td>
144
- </tr>
145
- </tbody>
146
- </table>
147
- </div>
148
- </template>
149
- ```
150
-
151
- Source: `examples/vue/virtualized-rows/src/App.vue`.
152
-
153
- ## Core Patterns
154
-
155
- ### 1. Keep `useVirtualizer` in the deepest possible component
156
-
157
- ```vue
158
- <!-- ✅ Body owns the virtualizer. Filter input changes in App.vue don't re-run it. -->
159
- <!-- App.vue -->
160
- <TableContainer ref="tableContainerRef">
161
- <TableBody :table="table" :container-ref="tableContainerRef" />
162
- </TableContainer>
163
-
164
- <!-- TableBody.vue: useVirtualizer lives here. -->
165
- ```
166
-
167
- This is the single most important perf rule. Putting `useVirtualizer` in the same component
168
- as `useTable` means any state change in that component re-runs the virtualizer, blowing
169
- scroll position and measurement cache.
170
-
171
- ### 2. `display: grid` + absolute-positioned rows
172
-
173
- Semantic `<table>` markup still works, but the _layout_ must be CSS grid + flexbox. Without
174
- `display: grid` on `<table>`/`<thead>`/`<tbody>` plus `position: absolute` + `transform:
175
- translateY(start)px` on each row, virtual rows stack or overlap.
176
-
177
- ### 3. Column virtualization
178
-
179
- ```ts
180
- const columnVirtualizer = useVirtualizer(
181
- computed(() => ({
182
- count: columns.length,
183
- estimateSize: (index) => columns[index].size ?? 150,
184
- getScrollElement: () => tableContainerRef.value,
185
- horizontal: true,
186
- overscan: 3,
187
- })),
188
- )
189
-
190
- const virtualCols = computed(() => columnVirtualizer.value.getVirtualItems())
191
-
192
- // Then in template, render only virtualCols. Pad with empty cells at left/right:
193
- const virtualPaddingLeft = computed(() => virtualCols.value[0]?.start ?? 0)
194
- const virtualPaddingRight = computed(() => {
195
- const last = virtualCols.value[virtualCols.value.length - 1]
196
- return columnVirtualizer.value.getTotalSize() - (last?.end ?? 0)
197
- })
198
- ```
199
-
200
- Without left/right padding placeholder cells, visible columns slide left as you scroll because
201
- unrendered columns aren't taking up scroll space.
202
-
203
- ### 4. Dynamic row heights via `measureElement`
204
-
205
- ```ts
206
- const rowVirtualizerOptions = computed(() => ({
207
- count: rows.value.length,
208
- estimateSize: () => 33,
209
- getScrollElement: () => tableContainerRef.value,
210
- // Firefox returns inconsistent table-row heights — skip there.
211
- measureElement:
212
- typeof window !== 'undefined' &&
213
- navigator.userAgent.indexOf('Firefox') === -1
214
- ? (el) => el.getBoundingClientRect().height
215
- : undefined,
216
- overscan: 5,
217
- }))
218
- ```
219
-
220
- In templates, wire `measureElement` via a ref callback:
221
-
222
- ```vue
223
- <tr :ref="(el) => rowVirtualizer.value.measureElement(el as Element)">
224
- ...
225
- </tr>
226
- ```
227
-
228
- ### 5. Infinite scroll
229
-
230
- Combine `useInfiniteQuery` from `@tanstack/vue-query` with a scroll-event handler that calls
231
- `fetchNextPage()` when within ~500px of the bottom. **Set `manualSorting: true`** so a new
232
- query fires on sort changes — otherwise the table re-sorts already-fetched pages locally and
233
- scrambles order.
234
-
235
- ```ts
236
- const flatData = computed(
237
- () => infiniteQuery.data.value?.pages.flatMap((p) => p.rows) ?? [],
238
- )
239
-
240
- const onScroll = (e: Event) => {
241
- const t = e.target as HTMLDivElement
242
- if (
243
- t.scrollHeight - t.scrollTop - t.clientHeight < 500 &&
244
- !infiniteQuery.isFetching.value
245
- ) {
246
- infiniteQuery.fetchNextPage()
247
- }
248
- }
249
- ```
250
-
251
- Source: `examples/vue/virtualized-infinite-scrolling/`.
252
-
253
- ## Common Mistakes
254
-
255
- ### Putting `useVirtualizer` in the same component as `useTable` (CRITICAL)
256
-
257
- Any unrelated state change in that component re-runs the virtualizer. Move it down to a
258
- `TableBody.vue` component that takes the table as a prop.
259
-
260
- ### Forgetting `display: grid` on `<table>`/`<thead>`/`<tbody>` (CRITICAL)
261
-
262
- The semantic table layout fights with absolute positioning. Virtual rows stack on top of each
263
- other or overlap.
264
-
265
- ### Missing `transform: translateY(virtualRow.start)px` (CRITICAL)
266
-
267
- All rows render at `top: 0` and only the last few are visible. The virtualizer reports the
268
- correct `start` for each item; you must apply it.
269
-
270
- ### Using `measureElement` on Firefox (HIGH)
271
-
272
- Firefox returns inconsistent border-height measurements for `<tr>` elements — rows jitter on
273
- every scroll. Guard with `navigator.userAgent.indexOf('Firefox') === -1`.
274
-
275
- ### Passing options as a plain object instead of a `computed` / getter (HIGH — Vue-specific)
276
-
277
- ```ts
278
- // ❌ Static options — virtualizer doesn't re-run when rows.length changes.
279
- const rowVirtualizer = useVirtualizer({
280
- count: rows.value.length,
281
- // ...
282
- })
283
-
284
- // ✅ Reactive options.
285
- const rowVirtualizer = useVirtualizer(
286
- computed(() => ({
287
- count: rows.value.length,
288
- estimateSize: () => 33,
289
- getScrollElement: () => tableContainerRef.value,
290
- })),
291
- )
292
- ```
293
-
294
- ### Forgetting padding cells in column virtualization (HIGH)
295
-
296
- Without `virtualPaddingLeft` / `virtualPaddingRight` cells, columns slide horizontally as you
297
- scroll because unrendered columns aren't taking up scroll space.
298
-
299
- ### Forgetting `manualSorting: true` on infinite scroll (HIGH)
300
-
301
- The table re-sorts already-fetched pages every time a new page arrives, scrambling order.
302
-
303
- ### Reading `table.state` above the virtualizer (HIGH)
304
-
305
- Any reactive read of `table.state` in a parent component re-renders the parent → re-renders
306
- the virtualizer-owning child → loses scroll. Use the narrowest read at the lowest level
307
- (see `tanstack-table/vue/production-readiness`).
308
-
309
- ### Hallucinating React Virtual hooks in Vue code (CRITICAL)
310
-
311
- ```ts
312
- // ❌
313
- import { useVirtualizer } from '@tanstack/react-virtual'
314
-
315
- // ✅
316
- import { useVirtualizer } from '@tanstack/vue-virtual'
317
- ```
318
-
319
- Same name, Vue-specific reactivity contract — `useVirtualizer` returns a `Ref<Virtualizer>`
320
- in Vue, not a plain object.
321
-
322
- ### "API missing" — `getRowModel` returns nothing (CRITICAL — v9-specific)
323
-
324
- If `table.getRowModel().rows` is empty when data is loaded, the row-model feature for whatever
325
- slice you need (filtering/sorting/grouping) isn't registered. Add it to `tableFeatures({...})`
326
- and `rowModels`.
327
-
328
- ### Reimplementing virtualization manually (CRITICAL — #1 AI tell)
329
-
330
- Slice `rows` with `Array.slice(start, end)` based on scroll position is the classic
331
- re-invention. Use `useVirtualizer` — it handles overscan, dynamic heights, scroll-to-index,
332
- all of which the hand-rolled version skips.
333
-
334
- ## See Also
335
-
336
- - `tanstack-table/vue/production-readiness` — keep the virtualizer in a leaf component
337
- - `tanstack-table/vue/table-state` — narrow reads to avoid parent re-renders
338
- - `tanstack-table/vue/compose-with-tanstack-query` — infinite-scroll pairs with `useInfiniteQuery`
339
- - `tanstack-table/table-core/row-expanding` — virtualized + expanding interactions
340
- - `tanstack-table/table-core/column-layout` — column sizing/pinning + virtualization