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
Svelte
Version
Latest
Search...
+ K
Menu
Getting Started
Core APIs
Examples
Framework
Svelte
Version
Latest
Menu
Getting Started
Core APIs
Examples
Svelte Example: Table
=========================================================================================================================================================================================================================================================================================================================================================================================================
Code ExplorerCode
Interactive SandboxSandbox
src
App.svelte
app.css
main.ts
makeData.ts
vite-env.d.ts
.gitignore
README.md
index.html
package.json
svelte.config.js
tsconfig.json
tsconfig.node.json
vite.config.ts
svelte
<script lang="ts">
import {
createSvelteTable,
getCoreRowModel,
getSortedRowModel,
type ColumnDef,
type SortingState,
type Updater,
flexRender,
type TableOptions,
} from '@tanstack/svelte-table'
import { createVirtualizer } from '@tanstack/svelte-virtual'
import { makeData, type Person } from './makeData'
import { writable } from 'svelte/store'
let virtualListEl: HTMLDivElement
let sorting: SortingState = []
function setSorting(updater: Updater<SortingState>) {
if (updater instanceof Function) sorting = updater(sorting)
else sorting = updater
tableOptions.update((opts) => ({
...opts,
state: {
...opts.state,
sorting,
},
}))
}
const columns: ColumnDef<Person>[] = [\
{\
accessorKey: 'id',\
header: 'ID',\
size: 60,\
},\
{\
accessorKey: 'firstName',\
header: 'First Name',\
cell: (info) => info.getValue(),\
},\
{\
accessorFn: (row) => row.lastName,\
id: 'lastName',\
header: 'Last Name',\
cell: (info) => info.getValue(),\
},\
{\
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 tableOptions = writable<TableOptions<Person>>({
data: makeData(50_000),
columns,
state: {
sorting,
},
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
debugTable: true,
})
const table = createSvelteTable(tableOptions)
$: rows = $table.getRowModel().rows
$: virtualizer = createVirtualizer<HTMLDivElement, HTMLTableRowElement>({
count: rows.length,
getScrollElement: () => virtualListEl,
estimateSize: () => 34,
overscan: 20,
})
</script>
<main>
<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 class="list scroll-container" bind:this={virtualListEl}>
<div style="position: relative; height: {$virtualizer.getTotalSize()}px;">
<table>
<thead>
{#each $table.getHeaderGroups() as headerGroup (headerGroup.id)}
<tr>
{#each headerGroup.headers as header (header.id)}
<th
colspan={header.colSpan}
style="width: {header.getSize()}px;"
>
{#if !header.isPlaceholder}
<button
class:sortable-header={header.column.getCanSort()}
disabled={!header.column.getCanSort()}
on:click={header.column.getToggleSortingHandler()}
>
<svelte:component
this={flexRender(
header.column.columnDef.header,
header.getContext(),
)}
/>
{#if header.column.getIsSorted()}
{header.column.getIsSorted() === 'desc' ? ' 🔽' : ' 🔼'}
{/if}
</button>
{/if}
</th>
{/each}
</tr>
{/each}
</thead>
<tbody>
{#each $virtualizer.getVirtualItems() as row, idx (row.index)}
<tr
style="height: {row.size}px; transform: translateY({row.start -
idx * row.size}px);"
>
{#each rows[row.index].getVisibleCells() as cell (cell.id)}
<td>
<svelte:component
this={flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
/>
</td>
{/each}
</tr>
{/each}
</tbody>
</table>
</div>
</div>
</main>
<style>
button {
background: none;
border: none;
padding: 0;
margin: 0;
font: inherit;
color: inherit;
cursor: pointer;
outline: inherit;
}
.scroll-container {
height: 600px;
width: 100%;
overflow: auto;
}
.sortable-header {
cursor: pointer;
user-select: none;
}
</style>
<script lang="ts">
import {
createSvelteTable,
getCoreRowModel,
getSortedRowModel,
type ColumnDef,
type SortingState,
type Updater,
flexRender,
type TableOptions,
} from '@tanstack/svelte-table'
import { createVirtualizer } from '@tanstack/svelte-virtual'
import { makeData, type Person } from './makeData'
import { writable } from 'svelte/store'
let virtualListEl: HTMLDivElement
let sorting: SortingState = []
function setSorting(updater: Updater<SortingState>) {
if (updater instanceof Function) sorting = updater(sorting)
else sorting = updater
tableOptions.update((opts) => ({
...opts,
state: {
...opts.state,
sorting,
},
}))
}
const columns: ColumnDef<Person>[] = [\
{\
accessorKey: 'id',\
header: 'ID',\
size: 60,\
},\
{\
accessorKey: 'firstName',\
header: 'First Name',\
cell: (info) => info.getValue(),\
},\
{\
accessorFn: (row) => row.lastName,\
id: 'lastName',\
header: 'Last Name',\
cell: (info) => info.getValue(),\
},\
{\
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 tableOptions = writable<TableOptions<Person>>({
data: makeData(50_000),
columns,
state: {
sorting,
},
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
debugTable: true,
})
const table = createSvelteTable(tableOptions)
$: rows = $table.getRowModel().rows
$: virtualizer = createVirtualizer<HTMLDivElement, HTMLTableRowElement>({
count: rows.length,
getScrollElement: () => virtualListEl,
estimateSize: () => 34,
overscan: 20,
})
</script>
<main>
<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 class="list scroll-container" bind:this={virtualListEl}>
<div style="position: relative; height: {$virtualizer.getTotalSize()}px;">
<table>
<thead>
{#each $table.getHeaderGroups() as headerGroup (headerGroup.id)}
<tr>
{#each headerGroup.headers as header (header.id)}
<th
colspan={header.colSpan}
style="width: {header.getSize()}px;"
>
{#if !header.isPlaceholder}
<button
class:sortable-header={header.column.getCanSort()}
disabled={!header.column.getCanSort()}
on:click={header.column.getToggleSortingHandler()}
>
<svelte:component
this={flexRender(
header.column.columnDef.header,
header.getContext(),
)}
/>
{#if header.column.getIsSorted()}
{header.column.getIsSorted() === 'desc' ? ' 🔽' : ' 🔼'}
{/if}
</button>
{/if}
</th>
{/each}
</tr>
{/each}
</thead>
<tbody>
{#each $virtualizer.getVirtualItems() as row, idx (row.index)}
<tr
style="height: {row.size}px; transform: translateY({row.start -
idx * row.size}px);"
>
{#each rows[row.index].getVisibleCells() as cell (cell.id)}
<td>
<svelte:component
this={flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
/>
</td>
{/each}
</tr>
{/each}
</tbody>
</table>
</div>
</div>
</main>
<style>
button {
background: none;
border: none;
padding: 0;
margin: 0;
font: inherit;
color: inherit;
cursor: pointer;
outline: inherit;
}
.scroll-container {
height: 600px;
width: 100%;
overflow: auto;
}
.sortable-header {
cursor: pointer;
user-select: none;
}
</style>
