Table Reference
API reference
One page, anchored: exports, DataTable props, Column options, table state, types, messages, the engine and the modules. Descriptions are checked against the package types by a test, so a renamed prop cannot keep a stale row.
- Fixture
On this page
Exports
The root entry ships the renderer, the engine, the pure helpers it is built from, and the two message sets. The ./core entry contains the pure functions only and imports nothing from Svelte.
Root entry
import { … } from '@svelte-lean/table'
| Name | Type | Default | Description |
|---|---|---|---|
# DataTable | Svelte component | – | The renderer: a native <table> over createTable(). |
# createTable | (options: TableOptions<T>) => Table<T> | – | The reactive engine below the renderer (runes; needs the Svelte compiler). |
# createRowModel | (context: ModelContext<T>) => RowModel<T> | – | The default row model: search, filters, sorting, tree flattening, row pinning, pagination. |
# defaultState | (patch?: Partial<TableState>) => TableState | – | A complete state object with optional overrides. |
# valueOf | (row: T, column: Column<T>) => unknown | – | Reads a cell value through the column accessor (field, dot path or function). |
# visibleColumns | (columns, state) => Column<T>[] | – | Leaf columns that are not hidden, in pin zone and user order. |
# toggleSort | (sorting: Sort[], id: string, multi?: boolean, descFirst?: boolean) => Sort[] | – | Pure sort cycle none, ascending, descending, none; descFirst starts descending. |
# toggleSelection | (current: RowId[], eligible: RowId[], select?: boolean) => RowId[] | – | Adds or removes the eligible ids; without select it selects unless all are selected. |
# en | TableMessages | – | English messages, the default of the renderer. |
# tr | TableMessages | – | Turkish messages. |
Core entry
import { … } from '@svelte-lean/table/core' — pure functions over the same types; runs under plain Node.
| Name | Type | Default | Description |
|---|---|---|---|
# leafColumns | (columns) => Column<T>[] | – | Flattens nested column groups to their leaves. |
# headerRows | (columns, visible) => HeaderCell<T>[][] | – | Header rows with colSpan and rowSpan; a group split by reordering or pinning renders as separate fragments. |
# matchesFilter | (value, filter: Filter, locale?) => boolean | – | The default filter predicate for every FilterOperator. |
# fold | (value, locale?) => string | – | Case and accent folding used by search and text filters (Turkish dotted and dotless i included). |
# prepareRows | (context: ModelContext<T>) => RowNode<T>[] | – | Filtered and sorted root nodes before pagination; groupBy() builds on it. |
# paginateNodes | (nodes, context) => RowModel<T> | – | Applies row pinning, page slicing and expansion to prepared nodes. |
# flattenRows | (nodes, expanded?) => RowNode<T>[] | – | Depth-first flattening; with an expanded set, collapsed children are skipped. |
# clampWidth | (column, width) => number | – | Clamps a width to minWidth and maxWidth (defaults 64 and 1200). |
# columnWidth | (column, state) => number | – | The rendered width: state.widths, then column.width, then 160, clamped. |
# toggleId | (ids: RowId[], id: RowId) => RowId[] | – | Adds or removes one id. |
# sortOf | (sorting: Sort[], column: Pick<Column<T>, 'id' | 'sortKeys'>) => { id; desc; index } | undefined | – | The first sort rule on the column id or one of its sortKeys, with its precedence. |
# reorder | (items, source, target) => T[] | – | Moves source before target in a copy of the list. |
DataTable props
Every capability of the renderer is a prop. The three required props establish the row contract; everything else defaults to off or to the plain table.
Data and identity
The rows, the schema and a stable identity per row.
| Name | Type | Default | Description |
|---|---|---|---|
# data required | readonly T[] | – | Rows of the current client or server page. |
# columns required | readonly Column<T>[] | – | Typed column schema; nested children produce grouped headers. |
# getRowId required | (row: T) => RowId | – | Returns a finite number or a string that stays stable across renders; selection, expansion, pinning and edits are keyed by it. Duplicates throw. |
# getSubRows | (row: T) => readonly T[] | undefined | undefined | Child rows for tree tables. Not combinable with groupBy(). |
# getRowModel | (context: ModelContext<T>) => RowModel<T> | createRowModel | Replaces the client row model; groupBy() from the grouping entry is the shipped alternative. |
# id | string | undefined | id of the root element. With selection: 'single' it also names the row radios (<id>-selection) so they form one native radio group. Authored, never generated. |
Controlled state
Bind one serializable object or listen without binding.
| Name | Type | Default | Description |
|---|---|---|---|
# state | Partial<TableState> | {} | Sorting, filters, search, paging, selection, expansion and column layout. Missing fields take the defaultState() values. Bindable. |
# onStateChange | (state: TableState) => void | undefined | Runs after every table-originated change with the complete next state. |
# manual | boolean | false | Treats data as already sorted, filtered and paged by the application; the client model applies none of them. |
# rowCount | number | undefined | Total row count in manual mode, for the page count, the status line and aria-rowcount. Without it the status line leaves the total out. |
# pageCount | number | from rowCount | Manual mode: the page count when the total is unknown, for example pageIndex + 2 while the server reports a next page. |
# pagination | boolean | true | Shows the pager and slices pages. false renders the whole row model on one page. |
# pageSizeOptions | number[] | [10, 20, 50, 100] | Sizes offered by the page-size select; the current size is always added. 0 is the all-rows option. |
Controls
Each control is independent; none is rendered by default.
| Name | Type | Default | Description |
|---|---|---|---|
# searchable | boolean | false | Shows the search input. state.search applies without it, so a search box elsewhere in the page can write the state. |
# searchDelay | number | 0 | Milliseconds between the last keystroke in the search box or a filter input and the state change; clearing a field applies at once. |
# searchText | (row: T) => string | undefined | The text search terms are matched against, per row. Without it: the values of the searchable, visible columns. |
# filters | boolean | false | Adds a filter input under each filterable leaf header (operator contains). |
# columnControls | boolean | false | Shows the visibility, order and pin controls in a <details> panel. |
# selection | false | 'single' | 'multiple' | false | Adds a radio or checkbox column. 'single' needs the id prop for the radio group name. |
# selectionScope | 'page' | 'filtered' | 'page' | Rows that select-all and the header checkbox consider: the current page or every filtered row. |
# isRowSelectable | (row: T) => boolean | () => true | Disables the selection control of rows that fail the rule. |
# hideSelectAll | boolean | false | Hides the header checkbox of multiple selection; the header cell keeps the selectionHeader text for assistive technology. |
# rowLabel | (row: T) => string | the row id | Accessible name of a row in its selection, expand and move controls. |
Expansion
Details rows and tree rows share the expanded id list in the state.
| Name | Type | Default | Description |
|---|---|---|---|
# rowDetails | Snippet<[T, RowDetailsContext]> | undefined | Renders a full-width details row under an expanded row. The second argument carries rowId, index and collapse(). A DataTable inside it keeps its own styles and keyboard behavior. |
# expandColumn | boolean | false | Renders the expand toggle in its own narrow leading column (after selection) instead of the first data column. |
# rowExpandable | (row: T) => boolean | () => true | Whether a row may show details. |
# expandRowByClick | boolean | false | Toggles expansion when a non-interactive part of a cell is clicked. |
# indentSize | number | 18 | Pixels added to the first cell per tree depth. |
# onExpand | (expanded: boolean, row: T) => void | undefined | Runs when a data row is expanded or collapsed. |
# onExpandedRowsChange | (rows: RowId[]) => void | undefined | Runs with the complete next expanded id list, group rows included. |
Presentation
Structure and naming stay explicit props; colors and spacing are CSS custom properties.
| Name | Type | Default | Description |
|---|---|---|---|
# label | string | 'Data table' | Accessible name of the scroll region and the <table>. |
# caption | string | undefined | Visible native <caption>. |
# title | Snippet<[RowModel<T>]> | undefined | Content above the toolbar with the current row model. |
# showHeader | boolean | true | Renders the <thead>. |
# tableLayout | 'auto' | 'fixed' | 'fixed' | The CSS table-layout of the <table>. fixed applies every column width; auto leaves columns without width, resize or pin to the browser, which sizes them from content. |
# layout | 'table' | 'grid' | 'table' | table: native table layout. grid: the same elements laid out as CSS grid rows with explicit ARIA roles; body rows stand in sections the browser skips off screen (content-visibility: auto), and column widths are measured from the content once and kept. width is a minimum, maxWidth caps a measured width (default 480), span and tableLayout do not apply. For pages of hundreds of rows. |
# density | 'compact' | 'comfortable' | 'spacious' | 'comfortable' | Cell padding and font size (compact: 13px), as data-density on the root. |
# striped | boolean | false | Alternate row backgrounds. |
# bordered | boolean | false | Vertical cell borders. |
# stickyHeader | boolean | true | Keeps the <thead> in view. With height it sticks inside the scroll region; without, it sticks to the page while the table fits its width (offset by --slean-table-sticky-top). A table wider than its container scrolls sideways in the region, which captures sticky positioning: the header then scrolls with the rows unless the region also scrolls down. |
# height | string | undefined | max-height of the scroll region; with it the region is a Tab stop. |
# progressive | boolean | number | false | Progressive mounting: when at least a first batch of rows the table has not drawn arrives (true = one screenful, or the number given), that batch renders at once and the rest follow in slices sized from the measured cost of a row, one macrotask apart, so a large page never blocks the main thread. A first render in the browser mounts the same way; server output and its hydration are always complete. |
# rowHoverable | boolean | true | Row hover background. |
# dir | 'ltr' | 'rtl' | undefined | Text direction of the table. Without it the root carries no dir attribute and keyboard, resize and pinning follow the nearest dir attribute in the document. |
# locale | string | undefined | Locale of the collator used for sorting and of case folding in search and filters. |
# messages | Partial<TableMessages> | en | Overrides of the visible and accessible strings; missing keys fall back to en. |
# placeholder | string | '' | Text of an empty value (null, undefined, empty string, or a format result of null or empty string). Not applied to cell snippets. |
# class | string | '' | Extra class on the root element. |
States
Network ownership stays with the application; the table announces what it is told.
| Name | Type | Default | Description |
|---|---|---|---|
# loading | boolean | false | Announces the loading status and marks the table aria-busy; without rows it draws skeletonRows placeholder rows. |
# fetching | boolean | false | Background refresh: status, aria-busy and data-fetching on the root (the body dims) while rows stay visible. |
# skeletonRows | number | 5 | Placeholder rows drawn while loading without rows; 0 shows the loading message instead. |
# error | string | undefined | Text of an alert rendered above the table. |
# onRetry | () => void | undefined | Adds the retry button to the alert. |
# empty | Snippet<[EmptyContext]> | undefined | Content of the single body cell when the row model has no rows. Receives search, filtered and loading to tell no matches from no data. |
Events and composition
Side effects leave through callbacks; content enters through snippets.
| Name | Type | Default | Description |
|---|---|---|---|
# onEdit | (event: EditEvent<T>) => void | Promise<void> | undefined | Receives a parsed and validated cell edit; enables editing and cell focus. The table never mutates rows. |
# onRowClick | (row: T, event: MouseEvent | KeyboardEvent, index: number) => void | undefined | Runs for clicks outside controls (button, a, input, select, textarea, label, summary, contenteditable, menuitem, [data-action], [data-no-row-click]). Rows become Tab stops answering Enter and Space unless the table has cell navigation. |
# onRowDoubleClick | (row: T, event: MouseEvent) => void | undefined | Runs on a row double-click. |
# onRowContextMenu | (row: T, event: MouseEvent) => void | undefined | Runs on the contextmenu event of a row; the table does not prevent the default. |
# onAction | (action: string, row: T, trigger: HTMLElement, index: number) => void | undefined | Delegated click on any element inside a cell that carries data-action. |
# onRowMove | (source: RowId, target: RowId) => void | undefined | Enables the move buttons and drag and drop while no sort, filter, search, tree or custom row model is active. |
# onScroll | (event: Event) => void | undefined | Scroll of the table region. |
# toolbar | Snippet<[RowModel<T>]> | undefined | Content in the toolbar beside the built-in controls. |
# footer | Snippet<[RowModel<T>]> | undefined | Content after the pager. |
# summary | Record<string, string | number> | undefined | A <tfoot> row keyed by leaf column id; summarize() from the grouping entry produces one. |
# rowClass | (row: T, index: number) => string | undefined | Class of a row, for application state or animation. A class that sets --slean-table-row-bg tints every cell of the row, pinned cells included. |
# cellRange | CellRange | null | undefined | The highlighted rectangular range; with onCellRangeChange cells become focusable. |
# onCellRangeChange | (range: CellRange | null) => void | undefined | Reports a click, Shift+click or Shift+arrow range. |
Methods
Exported from the component instance (bind:this).
| Name | Type | Default | Description |
|---|---|---|---|
# scrollTo | (config: { key?: RowId; index?: number; top?: number; offset?: number; align?: ScrollLogicalPosition }) => void | – | Scrolls the region to a row by id or index, or to a pixel offset. |
Column
A column reads a field, a dot path or a computed value, and declares how it sorts, filters, renders and edits. Columns are plain objects; only snippets tie them to a Svelte file.
Data and content
Identity, the value it reads and how the value is rendered.
| Name | Type | Default | Description |
|---|---|---|---|
# id required | string | – | Stable identity used by state fields and by every helper. |
# header required | string | – | Header text and the label of its controls. |
# accessor | keyof T | string | ((row: T) => unknown) | column.id | A field, a dot path or a function. Function accessors cannot be edited through applyEdit(). |
# children | Column<T>[] | undefined | Nested columns; the parent renders as a group header. |
# cell | Snippet<[CellContext<T>]> | undefined | Custom body cell with row, value, rowId, index and column. |
# headerCell | Snippet<[HeaderContext<T>]> | undefined | Custom leaf header content. Receives the column, its sort, the sort list, toggleSort() and setSorting(); with sortable: false it can host a sort control of its own. |
# format | (value: unknown, row: T) => string | null | String(value) | Formats the plain text of a cell and of a CSV export without a snippet; null or an empty string renders the placeholder. |
Layout
Widths and pinning use logical insets, so they work in both directions.
| Name | Type | Default | Description |
|---|---|---|---|
# width | number | 160 | Initial width in pixels. Optional with tableLayout auto, where a column without one sizes to content; pinned columns fall back to 160 for their offsets. |
# minWidth | number | 64 | Lower bound of resizing. |
# maxWidth | number | 1200 | Upper bound of resizing. |
# align | 'start' | 'center' | 'end' | 'start' | Logical text alignment. |
# pin | 'left' | 'right' | false | false | Initial sticky edge; state.pinning overrides it. |
# hideable | boolean | true | Whether the column controls may hide it. |
# resizable | boolean | true | Whether the header shows a resize handle. |
# ellipsis | boolean | { showTitle?: boolean } | false | Clips overflow with an ellipsis; showTitle exposes the full text as a title attribute. |
# span | (context: CellContext<T>) => CellSpan | undefined | undefined | rowspan and colspan of a cell; a zero suppresses the cell covered by a span. |
# class | string | ((row: T) => string) | '' | Class on every body cell of the column, or one computed per row. |
# headerClass | string | undefined | Class on the leaf header cell. |
Sort, filter and aggregate
Defaults cover numbers, dates and text; each can be replaced per column.
| Name | Type | Default | Description |
|---|---|---|---|
# sortable | boolean | true | Header sorting; empty values sort last in both directions. |
# compare | (a: unknown, b: unknown, aRow: T, bRow: T) => number | numbers, dates, then a locale collator | Custom comparator; the sort direction is applied to its result. |
# sortDescFirst | boolean | false | The first activation sorts descending: descending, ascending, none. |
# sortKeys | string[] | undefined | Further sort ids that count as this column sorted (aria-sort, the icon), for a headerCell that sorts by several fields. |
# filterable | boolean | true | Gets a filter input when filters is on. |
# filter | (value: unknown, filter: Filter, row: T) => boolean | matchesFilter | Custom predicate instead of the operator matcher. |
# searchable | boolean | true | Included in the search. |
# aggregate | 'sum' | 'mean' | 'min' | 'max' | 'count' | ((values: unknown[]) => unknown) | undefined | Aggregate used by groupBy() and summarize(). |
Editing
The renderer parses and validates; the application commits.
| Name | Type | Default | Description |
|---|---|---|---|
# editable | boolean | ((row: T) => boolean) | false | Cell editing for the column or per row; needs onEdit on the table. |
# editor | 'text' | 'number' | 'date' | 'select' | 'text' | Native input type of the editor. |
# options | { label: string; value: string }[] | [] | Options of the select editor. |
# parse | (value: string, row: T) => unknown | parseEdit | Converts the editor text to the domain value. |
# validate | (value: unknown, row: T) => string | undefined | undefined | Returns an error message that blocks onEdit and is shown in the editor. |
Table state
TableState is one plain, serializable object. Every change passes through setState() and onStateChange; bind:state on the renderer is the same path.
TableState
Fields and their defaultState() values.
| Name | Type | Default | Description |
|---|---|---|---|
# sorting | Sort[] | [] | Ordered sort rules; the array order is the multi-sort precedence. |
# filters | Filter[] | [] | Column filters with operator and value. |
# search | string | '' | The search text; terms are split on whitespace. |
# pageIndex | number | 0 | Zero-based page. |
# pageSize | number | 20 | Rows per page; 0 shows every row. |
# selection | RowId[] | [] | Selected row ids. |
# expanded | RowId[] | [] | Expanded details, tree and group row ids. |
string[] | [] | Hidden column ids. | |
# order | string[] | [] | Explicit leaf column order. |
# widths | Record<string, number> | {} | Resized widths by column id. |
# pinning | Record<string, Pin> | {} | Pin overrides by column id. |
# rowPinning | { top: RowId[]; bottom: RowId[] } | { top: [], bottom: [] } | Rows kept at the start or end of every page. |
Types
Event payloads and model types, all exported from the root entry.
Events and positions
Small typed values suited to stores, query layers and command handlers.
| Name | Type | Default | Description |
|---|---|---|---|
# EditEvent<T> | { row: T; rowId: RowId; columnId: string; previous: unknown; value: unknown } | – | The parsed edit passed to onEdit and produced by pasteRange(). |
# CellPosition | { row: RowId; column: string } | – | A cell by row id and column id. |
# CellRange | { start: CellPosition; end: CellPosition } | – | An inclusive rectangle anchored by two positions. |
# Sort | { id: string; desc: boolean } | – | One sort rule. |
# Filter | { id: string; operator?: FilterOperator; value: unknown } | – | One column filter. |
# FilterOperator | 'contains' | 'equals' | 'startsWith' | 'gt' | 'gte' | 'lt' | 'lte' | 'between' | 'in' | 'empty' | – | Operators of matchesFilter(); 'between' takes a two-element array, 'in' an array. |
# RowId | string | number | – | Row identity; numbers must be finite. |
# Pin | 'left' | 'right' | false | – | A pin edge. |
# Aggregate | 'sum' | 'mean' | 'min' | 'max' | 'count' | ((values: unknown[]) => unknown) | – | An aggregate name or function. |
Row model
What createRowModel() and a custom getRowModel exchange.
| Name | Type | Default | Description |
|---|---|---|---|
# ModelContext<T> | { data; columns; state; getRowId; getSubRows?; manual?; rowCount?; pageCount?; searchText?; locale? } | – | Input of a row model function. |
# RowModel<T> | { rows: RowNode<T>[]; total: number; pageCount: number; pageIndex: number; all: RowNode<T>[] } | – | The page to render, the filtered total and every filtered data row. |
# RowNode<T> | { id; data?; depth; children; kind: 'row' | 'group'; label?; count?; aggregates? } | – | A rendered row or a group row. |
# CellContext<T> | { row: T; value: unknown; rowId: RowId; index: number; column: Column<T> } | – | Argument of cell snippets and span callbacks. |
# CellSpan | { rows?: number; columns?: number } | – | Result of a span callback. |
# HeaderContext<T> | { column; sort: { id; desc; index } | undefined; sorting: Sort[]; toggleSort(multi?); setSorting(sorting) } | – | Argument of headerCell snippets. |
# EmptyContext | { search: string; filtered: boolean; loading: boolean } | – | Argument of the empty snippet; filtered is true while a column filter holds a value. |
# RowDetailsContext | { rowId: RowId; index: number; collapse: () => void } | – | Second argument of the rowDetails snippet. |
# HeaderCell<T> | { key: string; column: Column<T>; colSpan: number; rowSpan: number } | – | One header cell as rendered. |
Messages
Every visible or accessible string of the renderer comes from TableMessages. Pass a partial object to override some of them; en and tr are complete sets. Defaults shown are the en values.
TableMessages
Keys, where each one is used, and the English default.
| Name | Type | Default | Description |
|---|---|---|---|
# search | string | 'Search rows' | Label and placeholder of the search input. |
# columns | string | 'Columns' | Summary of the column controls panel. |
# selectAll | string | 'Select all eligible rows' | Label of the header checkbox. |
# selectRow | string | 'Select row' | Prefix of each row control label (followed by the row id). |
# expand | string | 'Expand row' | Label of a collapsed row toggle. |
# collapse | string | 'Collapse row' | Label of an expanded row toggle. |
# previous | string | 'Previous page' | Pager button. |
# next | string | 'Next page' | Pager button. |
# first | string | 'First page' | Pager button. |
# last | string | 'Last page' | Pager button. |
# pageSize | string | 'Rows per page' | Label of the page-size select. |
# loading | string | 'Loading rows…' | Status text while loading or fetching. |
# empty | string | 'No matching rows' | Body text when no row matches. |
# retry | string | 'Retry' | Retry button in the error alert. |
# save | string | 'Save' | Editor button. |
# cancel | string | 'Cancel' | Editor button. |
# edit | string | 'Edit' | Prefix of the per-cell edit button label. |
# resize | string | 'Resize' | Prefix of the resize handle label. |
# moveLeft | string | 'Move earlier' | Column controls: move a column earlier. |
# moveRight | string | 'Move later' | Column controls: move a column later. |
# moveUp | string | 'Move up' | Row reorder button. |
# moveDown | string | 'Move down' | Row reorder button. |
# pin | string | 'Pin' | Prefix of the pin select label. |
# unpin | string | 'Unpinned' | Option of the pin select. |
# pinLeft | string | 'Pin start' | Option of the pin select. |
# pinRight | string | 'Pin end' | Option of the pin select. |
# allRows | string | 'All' | The pageSize: 0 option of the page-size select. |
# sortOrder | string | 'Sort order' | Read to assistive technology before the multi-sort precedence number. |
# selectionHeader | string | 'Selection' | Visually hidden header text of the selection column when it has no select-all checkbox. |
# expandHeader | string | 'Details' | Visually hidden header text of the expand column (expandColumn). |
# filter | (column: string) => string | (column) => `Filter ${column}` | Label of a column filter input. |
# page | (page: number, pages: number, rows: number | undefined) => string | (page, pages, rows) => `Page ${page} of ${pages}` + (rows === undefined ? '' : ` · ${rows} rows`) | The pager status line; rows is undefined when a manual table has no rowCount. |
# pageNumber | (page: number) => string | (page) => `Page ${page}` | Accessible name of a numbered page button. |
# selected | (count: number) => string | (count) => `${count} selected` | The live selection count in the toolbar. |
Engine
createTable() is the engine the renderer is built on. Options are read lazily, so a getter keeps the engine reactive to the caller state; a state getter makes it controlled.
Use the engine when the renderer's markup is not the markup you need: a different table
structure, a list, a chart legend, or a test that asserts the model. It is the escape hatch
under DataTable and the same code path, so the behavior matches.
<script lang="ts">
import { createTable, type Column } from '@svelte-lean/table';
type Member = { id: number; name: string; revenue: number };
let rows = $state<Member[]>(load());
const columns: Column<Member>[] = [
{ id: 'name', header: 'Member' },
{ id: 'revenue', header: 'Revenue', align: 'end' }
];
// Options are read lazily: a getter keeps the engine reactive to $state in the caller.
// A plain object as state makes the table own it; a state getter makes it controlled.
const table = createTable({
data: () => rows,
columns,
getRowId: (row) => row.id,
state: { pageSize: 10 },
onStateChange: (next) => console.log(next.sorting)
});
</script>
<!-- A renderer of your own: derived values are getters and update with the state. -->
<table>
<thead>
<tr>
{#each table.visibleColumns as column (column.id)}
<th aria-sort={table.sortOf(column.id)?.desc ? 'descending' : table.sortOf(column.id) ? 'ascending' : undefined}>
<button type="button" onclick={() => table.toggleSort(column.id)}>{column.header}</button>
</th>
{/each}
</tr>
</thead>
<tbody>
{#each table.model.rows as node (node.id)}
<tr>
{#each table.visibleColumns as column (column.id)}
<td>{String(valueOf(node.data!, column) ?? '')}</td>
{/each}
</tr>
{/each}
</tbody>
</table>
<p>Page {table.model.pageIndex + 1} of {table.model.pageCount}</p>// Controlled: the engine reads the caller's state and stores nothing itself.
let view = $state<Partial<TableState>>({});
const table = createTable({
data: () => rows,
columns,
getRowId: (row) => row.id,
state: () => view,
onStateChange: (next) => (view = next)
});
table.setSearch('rhye'); // reports { …view, search: 'rhye', pageIndex: 0 } through onStateChange
table.toggleSelection(1); // selection helpers respect the selection mode and scope options
table.model.total; // filtered count, recomputed from the getters// No Svelte at all: the pure model under plain Node, for tests or a server-side export.
import { createRowModel, defaultState } from '@svelte-lean/table/core';
const model = createRowModel({
data: rows,
columns,
state: defaultState({ sorting: [{ id: 'revenue', desc: true }], pageSize: 0 }),
getRowId: (row) => row.id
});
model.rows.map((node) => node.data);TableOptions
A MaybeGetter<V> is a value or a function returning it.
| Name | Type | Default | Description |
|---|---|---|---|
# data required | MaybeGetter<readonly T[]> | – | Rows. |
# columns required | MaybeGetter<readonly Column<T>[]> | – | Column schema. |
# getRowId required | (row: T) => RowId | – | Row identity. |
# getSubRows | (row: T) => readonly T[] | undefined | undefined | Tree children. |
# state | Partial<TableState> | (() => Partial<TableState>) | {} | A plain object is the initial state of a table that owns its state; a getter makes it controlled. |
# onStateChange | (state: TableState) => void | undefined | Reports every change, owned or controlled. |
# getRowModel | (context: ModelContext<T>) => RowModel<T> | createRowModel | Custom row model. |
# manual | MaybeGetter<boolean | undefined> | false | Manual mode. |
# rowCount | MaybeGetter<number | undefined> | undefined | Manual row count. |
# pageCount | MaybeGetter<number | undefined> | undefined | Manual page count when the total is unknown. |
# searchText | (row: T) => string | undefined | Searchable text of a row instead of the searchable columns. |
# locale | MaybeGetter<string | undefined> | undefined | Collator locale. |
# pagination | MaybeGetter<boolean | undefined> | true | false builds the model with every row on one page. |
# selection | MaybeGetter<SelectionMode | undefined> | 'multiple' | false makes every selection helper a no-op. |
# isRowSelectable | (row: T) => boolean | () => true | Selection rule. |
# selectionScope | MaybeGetter<'page' | 'filtered' | undefined> | 'page' | Rows that selectAll() and allSelected consider. |
Table
Derived values are getters (reactive under runes); transitions call setState() and report through onStateChange.
| Name | Type | Default | Description |
|---|---|---|---|
# state | TableState | – | The complete current state. |
# model | RowModel<T> | – | Paginated, sorted and filtered row nodes. |
# columns | readonly Column<T>[] | – | Column definitions as given, groups included. |
# leafColumns | Column<T>[] | – | Every leaf column. |
# visibleColumns | Column<T>[] | – | Leaf columns that are not hidden, in pin zone and user order. |
# headers | HeaderCell<T>[][] | – | Header rows with spans. |
# selectable | RowId[] | – | Ids a selection control may act on within selectionScope. |
# allSelected | boolean | – | Every selectable row is selected. |
# someSelected | boolean | – | At least one selectable row is selected. |
# isSelected | (id: RowId) => boolean | – | Selection membership. |
# isExpanded | (id: RowId) => boolean | – | Expansion membership. |
# sortOf | (column: string | Column<T>) => { id: string; desc: boolean; index: number } | undefined | – | Sort rule, direction and precedence of a column; given a column, a rule on one of its sortKeys counts. |
# pinOf | (column: Column<T>) => Pin | undefined | – | Effective pin of a column. |
# columnWidth | (column: Column<T>) => number | – | Effective width of a column. |
# setState | (patch: Partial<TableState>) => TableState | – | Merges a patch, stores it when the table owns its state, reports it. |
# toggleSort | (id: string, multi?: boolean) => void | – | Cycles none, ascending, descending, none (descending first with sortDescFirst); multi keeps other sorted columns. Resets the page. |
# toggleSelection | (id: RowId, selected?: boolean) => void | – | Toggles one row; in 'single' mode the row replaces the selection. |
# selectAll | (ids?: readonly RowId[], selected?: boolean) => void | – | Selects or clears many rows; defaults to selectable and to select unless all are selected. |
# toggleExpanded | (id: RowId, expanded?: boolean) => boolean | – | Returns the new expanded state. |
# setSearch | (search: string) => void | – | Sets the search text and resets the page. |
# setFilter | (id: string, value: unknown, operator?: FilterOperator) => void | – | Replaces the filter of a column; undefined removes it. Resets the page. |
# setPage | (index: number) => void | – | Clamped to the page count. |
# setPageSize | (size: number) => void | – | 0 shows every row. Resets the page. |
# moveColumn | (id: string, delta: number) => void | – | Moves a visible column delta places. |
# resizeColumn | (id: string, width: number) => void | – | Stores a clamped width. |
# pin | (id: string, pin: Pin) => void | – | Pins a column. |
# toggleColumn | (id: string, visible?: boolean) => void | – | Hides or shows a column. |
Modules
Separate entry points, one subpath each. They are pure functions over the same types, never import the renderer and stay out of a build until imported (proved by the table-basic fixture).
Entry points
Import path, exports and purpose. Sizes are on the overview page and in the size file.
| Name | Type | Default | Description |
|---|---|---|---|
# @svelte-lean/table/grouping | groupBy · summarize · aggregate | – | A grouped row model for getRowModel, a totals object for summary, and the aggregate function both use. |
# @svelte-lean/table/pivot | pivot | – | Long-form records to a cross-tab: rows, columns and totals for a second DataTable. |
# @svelte-lean/table/editing | applyEdit · setValue · parseEdit · createHistory · editEvent | – | Immutable edit application and a caller-owned undo history. |
# @svelte-lean/table/clipboard | copyRange · pasteRange · rangeBounds | – | A rectangular range to tab-separated text and back to validated EditEvents. |
# @svelte-lean/table/export | toCsv · parseDelimited · downloadText | – | CSV with quoting and formula protection, a parser, and a browser download. |
# @svelte-lean/table/server | createDataSource | – | Sequencing and abort of a loader the application supplies. |
# @svelte-lean/table/state | serializeState · restoreState | – | Versioned persistence of presentation state; unknown columns are dropped on restore. |
# @svelte-lean/table/style.css | stylesheet | – | Styles for the slean-table- classes; reads --slean-* tokens when present. |
Module sizes
Each entry bundled on its own with esbuild, Svelte's runtime excluded; shared code is counted
per entry here and de-duplicated by a consumer's bundler. Read from packages/table/artifacts/size.json.
| Entry point | gzip | brotli | Budget (gzip) |
|---|---|---|---|
@svelte-lean/table/grouping | 2148 B | 1963 B | 2400 B |
@svelte-lean/table/pivot | 526 B | 470 B | 600 B |
@svelte-lean/table/editing | 707 B | 617 B | 800 B |
@svelte-lean/table/clipboard | 895 B | 817 B | 1000 B |
@svelte-lean/table/export | 741 B | 646 B | 850 B |
@svelte-lean/table/server | 369 B | 310 B | 425 B |
@svelte-lean/table/state | 796 B | 711 B | 900 B |
@svelte-lean/table/style.css | 6665 B | 5704 B | 7300 B |