File: table.md | Updated: 11/15/2025
Search...
+ K
Auto
Docs Examples GitHub Contributors
Docs Examples GitHub Contributors
Docs Examples GitHub Contributors
Docs Examples Github Contributors
Docs Examples Github Contributors
Docs Examples Github Contributors
Docs Examples Github Contributors
Docs Examples Github Contributors
Maintainers Partners Support Learn StatsBETA Discord Merch Blog GitHub Ethos Brand Guide
Documentation
Framework
Vue
Version
Latest
Search...
+ K
Menu
Getting Started
Core APIs
Examples
Framework
Vue
Version
Latest
Menu
Getting Started
Core APIs
Examples
Vue Example: Table
=======================================================================================================================================================================================================================================================================================================================================================================================
Code ExplorerCode
Interactive SandboxSandbox
public
src
App.vue
main.ts
makeData.ts
style.css
vite-env.d.ts
.gitignore
README.md
index.html
package.json
tsconfig.json
tsconfig.node.json
vite.config.ts
vue
<template>
<div>
<p>
For tables, the basis for the offset of the translate css function is from
the row's initial position itself. Because of this, we need to calculate
the translateY pixel count differently and base it off the index.
</p>
<div ref="parentRef" class="container">
<div :style="{ height: `${totalSize}px` }">
<table>
<thead>
<tr
v-for="headerGroup in table.getHeaderGroups()"
:key="headerGroup.id"
>
<th
v-for="header in headerGroup.headers"
:key="header.id"
:colspan="header.colSpan"
:style="{ width: `${header.getSize()}px` }"
>
<div
v-if="!header.isPlaceholder"
:class="[\
'text-left',\
header.column.getCanSort()\
? 'cursor-pointer select-none'\
: '',\
]"
@click="
getSortingHandler(
$event,
header.column.getToggleSortingHandler(),
)
"
>
<FlexRender
:render="header.column.columnDef.header"
:props="header.getContext()"
/>
<span v-if="header.column.getIsSorted() === 'asc'"> 🔼</span>
<span v-if="header.column.getIsSorted() === 'desc'"> 🔽</span>
</div>
</th>
</tr>
</thead>
<tbody>
<tr
v-for="(virtualRow, index) in virtualRows"
:key="virtualRow.key"
:style="{
transform: `translateY(${virtualRow.start - index * virtualRow.size}px)`,
}"
>
<td
v-for="cell in rows[virtualRow.index].getVisibleCells()"
:key="cell.id"
>
<FlexRender
:render="cell.column.columnDef.cell"
:props="cell.getContext()"
/>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useVirtualizer } from '@tanstack/vue-virtual'
import {
FlexRender,
ColumnDef,
SortingState,
useVueTable,
getCoreRowModel,
getSortedRowModel,
} from '@tanstack/vue-table'
import { makeData, Person } from './makeData'
const data = ref(makeData(50_000))
const sorting = ref<SortingState>([])
const getSortingHandler = (e: Event, fn: any) => {
return fn(e)
}
const setSorting = (sortingUpdater: any) => {
const newSortVal = sortingUpdater(sorting.value)
sorting.value = newSortVal
}
const columns = computed<ColumnDef<Person>[]>(() => {
return [\
{\
accessorKey: 'id',\
header: 'ID',\
size: 60,\
},\
{\
accessorKey: 'firstName',\
cell: (info) => info.getValue(),\
},\
{\
accessorFn: (row) => row.lastName,\
id: 'lastName',\
cell: (info) => info.getValue(),\
header: () => 'Last Name',\
},\
{\
accessorKey: 'age',\
header: () => 'Age',\
size: 50,\
},\
{\
accessorKey: 'visits',\
header: () => 'Visits',\
size: 50,\
},\
{\
accessorKey: 'status',\
header: 'Status',\
},\
{\
accessorKey: 'progress',\
header: 'Profile Progress',\
size: 80,\
},\
{\
accessorKey: 'createdAt',\
header: 'Created At',\
cell: (info) => info.getValue<Date>().toLocaleString(),\
},\
]
})
const table = useVueTable({
get data() {
return data.value
},
columns: columns.value,
state: {
get sorting() {
return sorting.value
},
},
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
debugTable: true,
})
const rows = computed(() => {
return table.getRowModel().rows
})
const parentRef = ref<HTMLElement | null>(null)
const rowVirtualizerOptions = computed(() => {
return {
count: rows.value.length,
getScrollElement: () => parentRef.value,
estimateSize: () => 34,
overscan: 5,
}
})
const rowVirtualizer = useVirtualizer(rowVirtualizerOptions)
const virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())
const totalSize = computed(() => rowVirtualizer.value.getTotalSize())
</script>
<template>
<div>
<p>
For tables, the basis for the offset of the translate css function is from
the row's initial position itself. Because of this, we need to calculate
the translateY pixel count differently and base it off the index.
</p>
<div ref="parentRef" class="container">
<div :style="{ height: `${totalSize}px` }">
<table>
<thead>
<tr
v-for="headerGroup in table.getHeaderGroups()"
:key="headerGroup.id"
>
<th
v-for="header in headerGroup.headers"
:key="header.id"
:colspan="header.colSpan"
:style="{ width: `${header.getSize()}px` }"
>
<div
v-if="!header.isPlaceholder"
:class="[\
'text-left',\
header.column.getCanSort()\
? 'cursor-pointer select-none'\
: '',\
]"
@click="
getSortingHandler(
$event,
header.column.getToggleSortingHandler(),
)
"
>
<FlexRender
:render="header.column.columnDef.header"
:props="header.getContext()"
/>
<span v-if="header.column.getIsSorted() === 'asc'"> 🔼</span>
<span v-if="header.column.getIsSorted() === 'desc'"> 🔽</span>
</div>
</th>
</tr>
</thead>
<tbody>
<tr
v-for="(virtualRow, index) in virtualRows"
:key="virtualRow.key"
:style="{
transform: `translateY(${virtualRow.start - index * virtualRow.size}px)`,
}"
>
<td
v-for="cell in rows[virtualRow.index].getVisibleCells()"
:key="cell.id"
>
<FlexRender
:render="cell.column.columnDef.cell"
:props="cell.getContext()"
/>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useVirtualizer } from '@tanstack/vue-virtual'
import {
FlexRender,
ColumnDef,
SortingState,
useVueTable,
getCoreRowModel,
getSortedRowModel,
} from '@tanstack/vue-table'
import { makeData, Person } from './makeData'
const data = ref(makeData(50_000))
const sorting = ref<SortingState>([])
const getSortingHandler = (e: Event, fn: any) => {
return fn(e)
}
const setSorting = (sortingUpdater: any) => {
const newSortVal = sortingUpdater(sorting.value)
sorting.value = newSortVal
}
const columns = computed<ColumnDef<Person>[]>(() => {
return [\
{\
accessorKey: 'id',\
header: 'ID',\
size: 60,\
},\
{\
accessorKey: 'firstName',\
cell: (info) => info.getValue(),\
},\
{\
accessorFn: (row) => row.lastName,\
id: 'lastName',\
cell: (info) => info.getValue(),\
header: () => 'Last Name',\
},\
{\
accessorKey: 'age',\
header: () => 'Age',\
size: 50,\
},\
{\
accessorKey: 'visits',\
header: () => 'Visits',\
size: 50,\
},\
{\
accessorKey: 'status',\
header: 'Status',\
},\
{\
accessorKey: 'progress',\
header: 'Profile Progress',\
size: 80,\
},\
{\
accessorKey: 'createdAt',\
header: 'Created At',\
cell: (info) => info.getValue<Date>().toLocaleString(),\
},\
]
})
const table = useVueTable({
get data() {
return data.value
},
columns: columns.value,
state: {
get sorting() {
return sorting.value
},
},
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
debugTable: true,
})
const rows = computed(() => {
return table.getRowModel().rows
})
const parentRef = ref<HTMLElement | null>(null)
const rowVirtualizerOptions = computed(() => {
return {
count: rows.value.length,
getScrollElement: () => parentRef.value,
estimateSize: () => 34,
overscan: 5,
}
})
const rowVirtualizer = useVirtualizer(rowVirtualizerOptions)
const virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())
const totalSize = computed(() => rowVirtualizer.value.getTotalSize())
</script>
