A comprehensive, AI-configurable data table component for Svelte and SvelteKit, built on TanStack Table v8.
Svelte Table Kit brings Airtable-like functionality to your Svelte applications with a headless, fully customizable table component. Perfect for dashboards, data grids, and complex data visualization needs.
Core Table Features:
Advanced Filtering:
Sorting Options:
Grouping & Hierarchy:
Column Context Menu:
Developer Experience:
AI-Ready:
š View Development Roadmap - See what's coming next!
npm install @shotleybuilder/svelte-table-kit
Or using pnpm:
pnpm add @shotleybuilder/svelte-table-kit
<script>
import { TableKit } from '@shotleybuilder/svelte-table-kit';
const data = [
{ id: 1, name: 'Alice', role: 'Developer', age: 28 },
{ id: 2, name: 'Bob', role: 'Designer', age: 32 },
{ id: 3, name: 'Charlie', role: 'Manager', age: 45 }
];
const columns = [
{ accessorKey: 'id', header: 'ID' },
{ accessorKey: 'name', header: 'Name' },
{ accessorKey: 'role', header: 'Role' },
{ accessorKey: 'age', header: 'Age' }
];
</script>
<TableKit {data} {columns} storageKey="my-table" />
The simplest way to use TableKit:
<TableKit {data} {columns} />
Customize initial table state programmatically:
<script>
import { TableKit } from '@shotleybuilder/svelte-table-kit';
</script>
<TableKit
{data}
{columns}
config={{
id: 'my-view-v1',
version: '1.0',
defaultColumnOrder: ['name', 'role', 'age', 'id'],
defaultColumnSizing: { name: 200, role: 150 },
defaultVisibleColumns: ['name', 'role', 'age'],
defaultFilters: [
{ id: 'f1', field: 'role', operator: 'equals', value: 'Developer' }
],
defaultSorting: [
{ columnId: 'name', direction: 'asc' }
],
filterLogic: 'and'
}}
features={{
columnVisibility: true,
filtering: true,
sorting: true,
pagination: true
}}
/>
The config prop is fully reactive - update it dynamically to change table state without remounting:
<script>
import { TableKit } from '@shotleybuilder/svelte-table-kit';
let tableConfig = $state({
id: 'query-1',
version: '1.0',
defaultFilters: [
{ id: 'f1', field: 'status', operator: 'equals', value: 'active' }
]
});
// Update config - table reacts automatically
function showPendingItems() {
tableConfig = {
id: 'query-2', // New ID triggers update
version: '1.0',
defaultFilters: [
{ id: 'f1', field: 'status', operator: 'equals', value: 'pending' }
]
};
}
</script>
<button on:click={showPendingItems}>Show Pending</button>
<TableKit {data} {columns} config={tableConfig} persistState={false} />
Perfect for AI-driven tables:
<script>
let aiConfig = $state(undefined);
async function askAI(question) {
const response = await fetch('/api/nl-query', {
method: 'POST',
body: JSON.stringify({ question })
});
aiConfig = await response.json(); // Table updates automatically
}
</script>
<input
placeholder="Ask a question about the data..."
on:submit={(e) => askAI(e.target.value)}
/>
<TableKit {data} {columns} config={aiConfig} persistState={false} />
Key Points:
config.idpersistState={false} to prevent localStorage conflicts{#key} blocks needed - updates are smooth and instantControl which features are enabled:
<TableKit
{data}
{columns}
features={{
columnVisibility: true,
columnResizing: true,
columnReordering: true,
filtering: true,
sorting: true,
sortingMode: 'control', // 'header' (default) or 'control' (Airtable-style)
pagination: true,
rowSelection: false,
grouping: false
}}
/>
Sorting Modes:
sortingMode: 'header' - Click column headers to sort (default behavior)sortingMode: 'control' - Use Airtable-style sort dropdown with multi-level supportListen to table events:
<TableKit
{data}
{columns}
onRowClick={(row) => console.log('Clicked:', row)}
onRowSelect={(rows) => console.log('Selected:', rows)}
onStateChange={(state) => console.log('State:', state)}
/>
Add custom controls to the left side of the toolbar using the toolbar-left slot:
<script>
import { TableKit } from '@shotleybuilder/svelte-table-kit';
import ViewSelector from './ViewSelector.svelte';
</script>
<TableKit {data} {columns}>
<!-- Add custom controls to the toolbar -->
<svelte:fragment slot="toolbar-left">
<ViewSelector on:viewSelected={handleViewSelected} />
<button on:click={saveView} class="btn-primary">
Save View
</button>
</svelte:fragment>
</TableKit>
Use Cases:
The toolbar-left slot is positioned on the left side of the toolbar, while the built-in table controls (Filter, Sort, Group, Columns) automatically align to the right. All controls appear on the same row, creating a unified control bar.
TableKit is headless by default. You can:
<TableKit
{data}
{columns}
classNames={{
container: 'my-container',
table: 'my-table',
th: 'my-header'
}}
/>
<TableKit {data} {columns} theme="dark" />
| Prop | Type | Default | Description |
|---|---|---|---|
data |
T[] |
[] |
Table data array |
columns |
ColumnDef<T>[] |
[] |
Column definitions |
config |
TableConfig |
undefined |
Reactive table configuration (requires id and version) |
features |
TableFeatures |
All enabled | Feature flags |
storageKey |
string |
undefined |
LocalStorage key for persistence |
persistState |
boolean |
true |
Enable state persistence (auto-disabled when config is active) |
theme |
'light' | 'dark' | 'auto' |
'light' |
Theme mode |
align |
'left' | 'center' | 'right' |
'left' |
Column text alignment |
rowHeight |
'short' | 'medium' | 'tall' | 'extra_tall' |
'medium' |
Row height preset |
columnSpacing |
'narrow' | 'normal' | 'wide' |
'normal' |
Column horizontal spacing |
onRowClick |
(row: T) => void |
undefined |
Row click handler |
onRowSelect |
(rows: T[]) => void |
undefined |
Row selection handler |
onStateChange |
(state: TableState) => void |
undefined |
State change handler |
interface TableConfig {
id: string; // Required: Unique identifier for change detection
version: string; // Required: Config version
defaultColumnOrder?: string[]; // Column IDs in display order
defaultColumnSizing?: Record<string, number>; // Column widths in pixels
defaultVisibleColumns?: string[]; // Visible column IDs (others hidden)
defaultFilters?: FilterCondition[]; // Initial filter conditions
defaultSorting?: SortConfig[]; // Initial sort configuration
filterLogic?: 'and' | 'or'; // Filter combination logic
pagination?: {
pageSize: number;
pageSizeOptions?: number[];
};
}
Contributions are welcome! Please read our Contributing Guide (coming soon).
MIT Ā© Sertantai
Built with: