Overview
Svelte Headless Table is a headless, composable table library for Svelte. It gives you a powerful table view-model (sorting, filtering, grouping, selection, pagination, etc.) while leaving the DOM and styling entirely up to you.
Why headless?
- Control: You own the markup, accessibility, animations, and design system.
- Maintainability: A focused API without UI baggage stays small and predictable.
- Extensibility: Compose only what you need via plugins and your own components.
Core concepts
- createTable(data, plugins): Builds a table instance from your data (an array, or a getter for reactive data) and plugin config
- createColumns([…]): Declares how to read and present fields from your data items
- createViewModel(columns): Derives header rows, body rows, and attributes to spread on markup, as plain reactive values under
vm.current
Installation
Install with your package manager of choice:
npm i -D @humanspeak/svelte-headless-tablenpm i -D @humanspeak/svelte-headless-tableyarn add -D @humanspeak/svelte-headless-tableyarn add -D @humanspeak/svelte-headless-tablepnpm add -D @humanspeak/svelte-headless-tablepnpm add -D @humanspeak/svelte-headless-tableRequires Svelte 5.30 or later. Upgrading from 6.x? See Migrating to v7.
Minimal example
<script lang="ts">
import { createTable, Render } from '@humanspeak/svelte-headless-table'
const data = [
{ name: 'Ada Lovelace', age: 21 },
{ name: 'Barbara Liskov', age: 52 },
{ name: 'Richard Hamming', age: 38 }
]
const table = createTable(data)
const columns = table.createColumns([
table.column({ header: 'Name', accessor: 'name' }),
table.column({ header: 'Age', accessor: 'age' })
])
const vm = table.createViewModel(columns)
</script>
<table {...vm.current.tableAttrs}>
<thead>
{#each vm.current.headerRows as headerRow (headerRow.id)}
<tr {...headerRow.current.attrs}>
{#each headerRow.cells as cell (cell.id)}
<th {...cell.current.attrs}>
<Render of={cell.render()} />
</th>
{/each}
</tr>
{/each}
</thead>
<tbody {...vm.current.tableBodyAttrs}>
{#each vm.current.pageRows as row (row.id)}
<tr {...row.current.attrs}>
{#each row.cells as cell (cell.id)}
<td {...cell.current.attrs}>
<Render of={cell.render()} />
</td>
{/each}
</tr>
{/each}
</tbody>
</table><script lang="ts">
import { createTable, Render } from '@humanspeak/svelte-headless-table'
const data = [
{ name: 'Ada Lovelace', age: 21 },
{ name: 'Barbara Liskov', age: 52 },
{ name: 'Richard Hamming', age: 38 }
]
const table = createTable(data)
const columns = table.createColumns([
table.column({ header: 'Name', accessor: 'name' }),
table.column({ header: 'Age', accessor: 'age' })
])
const vm = table.createViewModel(columns)
</script>
<table {...vm.current.tableAttrs}>
<thead>
{#each vm.current.headerRows as headerRow (headerRow.id)}
<tr {...headerRow.current.attrs}>
{#each headerRow.cells as cell (cell.id)}
<th {...cell.current.attrs}>
<Render of={cell.render()} />
</th>
{/each}
</tr>
{/each}
</thead>
<tbody {...vm.current.tableBodyAttrs}>
{#each vm.current.pageRows as row (row.id)}
<tr {...row.current.attrs}>
{#each row.cells as cell (cell.id)}
<td {...cell.current.attrs}>
<Render of={cell.render()} />
</td>
{/each}
</tr>
{/each}
</tbody>
</table>Next steps
- Read the Quick Start
- Explore the API: createTable and createColumns
- Browse Plugins Overview