sveltelean Table
Versionv0.2.0 GitHub

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'

NameTypeDefaultDescription
# 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.

NameTypeDefaultDescription
# 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.

NameTypeDefaultDescription
# data requiredreadonly T[]–Rows of the current client or server page.
# columns requiredreadonly 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[] | undefinedundefinedChild rows for tree tables. Not combinable with groupBy().
# getRowModel (context: ModelContext<T>) => RowModel<T>createRowModelReplaces the client row model; groupBy() from the grouping entry is the shipped alternative.
# id stringundefinedid 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.

NameTypeDefaultDescription
# state Partial<TableState>{}Sorting, filters, search, paging, selection, expansion and column layout. Missing fields take the defaultState() values. Bindable.
# onStateChange (state: TableState) => voidundefinedRuns after every table-originated change with the complete next state.
# manual booleanfalseTreats data as already sorted, filtered and paged by the application; the client model applies none of them.
# rowCount numberundefinedTotal 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 numberfrom rowCountManual mode: the page count when the total is unknown, for example pageIndex + 2 while the server reports a next page.
# pagination booleantrueShows 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.

NameTypeDefaultDescription
# searchable booleanfalseShows the search input. state.search applies without it, so a search box elsewhere in the page can write the state.
# searchDelay number0Milliseconds 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) => stringundefinedThe text search terms are matched against, per row. Without it: the values of the searchable, visible columns.
# filters booleanfalseAdds a filter input under each filterable leaf header (operator contains).
# columnControls booleanfalseShows the visibility, order and pin controls in a <details> panel.
# selection false | 'single' | 'multiple'falseAdds 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() => trueDisables the selection control of rows that fail the rule.
# hideSelectAll booleanfalseHides the header checkbox of multiple selection; the header cell keeps the selectionHeader text for assistive technology.
# rowLabel (row: T) => stringthe row idAccessible 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.

NameTypeDefaultDescription
# rowDetails Snippet<[T, RowDetailsContext]>undefinedRenders 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 booleanfalseRenders the expand toggle in its own narrow leading column (after selection) instead of the first data column.
# rowExpandable (row: T) => boolean() => trueWhether a row may show details.
# expandRowByClick booleanfalseToggles expansion when a non-interactive part of a cell is clicked.
# indentSize number18Pixels added to the first cell per tree depth.
# onExpand (expanded: boolean, row: T) => voidundefinedRuns when a data row is expanded or collapsed.
# onExpandedRowsChange (rows: RowId[]) => voidundefinedRuns with the complete next expanded id list, group rows included.

Presentation

Structure and naming stay explicit props; colors and spacing are CSS custom properties.

NameTypeDefaultDescription
# label string'Data table'Accessible name of the scroll region and the <table>.
# caption stringundefinedVisible native <caption>.
# title Snippet<[RowModel<T>]>undefinedContent above the toolbar with the current row model.
# showHeader booleantrueRenders 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 booleanfalseAlternate row backgrounds.
# bordered booleanfalseVertical cell borders.
# stickyHeader booleantrueKeeps 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 stringundefinedmax-height of the scroll region; with it the region is a Tab stop.
# progressive boolean | numberfalseProgressive 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 booleantrueRow hover background.
# dir 'ltr' | 'rtl'undefinedText 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 stringundefinedLocale of the collator used for sorting and of case folding in search and filters.
# messages Partial<TableMessages>enOverrides 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.

NameTypeDefaultDescription
# loading booleanfalseAnnounces the loading status and marks the table aria-busy; without rows it draws skeletonRows placeholder rows.
# fetching booleanfalseBackground refresh: status, aria-busy and data-fetching on the root (the body dims) while rows stay visible.
# skeletonRows number5Placeholder rows drawn while loading without rows; 0 shows the loading message instead.
# error stringundefinedText of an alert rendered above the table.
# onRetry () => voidundefinedAdds the retry button to the alert.
# empty Snippet<[EmptyContext]>undefinedContent 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.

NameTypeDefaultDescription
# onEdit (event: EditEvent<T>) => void | Promise<void>undefinedReceives a parsed and validated cell edit; enables editing and cell focus. The table never mutates rows.
# onRowClick (row: T, event: MouseEvent | KeyboardEvent, index: number) => voidundefinedRuns 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) => voidundefinedRuns on a row double-click.
# onRowContextMenu (row: T, event: MouseEvent) => voidundefinedRuns on the contextmenu event of a row; the table does not prevent the default.
# onAction (action: string, row: T, trigger: HTMLElement, index: number) => voidundefinedDelegated click on any element inside a cell that carries data-action.
# onRowMove (source: RowId, target: RowId) => voidundefinedEnables the move buttons and drag and drop while no sort, filter, search, tree or custom row model is active.
# onScroll (event: Event) => voidundefinedScroll of the table region.
# toolbar Snippet<[RowModel<T>]>undefinedContent in the toolbar beside the built-in controls.
Snippet<[RowModel<T>]>undefinedContent after the pager.
# summary Record<string, string | number>undefinedA <tfoot> row keyed by leaf column id; summarize() from the grouping entry produces one.
# rowClass (row: T, index: number) => stringundefinedClass 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 | nullundefinedThe highlighted rectangular range; with onCellRangeChange cells become focusable.
# onCellRangeChange (range: CellRange | null) => voidundefinedReports a click, Shift+click or Shift+arrow range.

Methods

Exported from the component instance (bind:this).

NameTypeDefaultDescription
# 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.

NameTypeDefaultDescription
# id requiredstring–Stable identity used by state fields and by every helper.
# header requiredstring–Header text and the label of its controls.
# accessor keyof T | string | ((row: T) => unknown)column.idA field, a dot path or a function. Function accessors cannot be edited through applyEdit().
# children Column<T>[]undefinedNested columns; the parent renders as a group header.
# cell Snippet<[CellContext<T>]>undefinedCustom body cell with row, value, rowId, index and column.
# headerCell Snippet<[HeaderContext<T>]>undefinedCustom 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 | nullString(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.

NameTypeDefaultDescription
# width number160Initial 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 number64Lower bound of resizing.
# maxWidth number1200Upper bound of resizing.
# align 'start' | 'center' | 'end''start'Logical text alignment.
# pin 'left' | 'right' | falsefalseInitial sticky edge; state.pinning overrides it.
# hideable booleantrueWhether the column controls may hide it.
# resizable booleantrueWhether the header shows a resize handle.
# ellipsis boolean | { showTitle?: boolean }falseClips overflow with an ellipsis; showTitle exposes the full text as a title attribute.
# span (context: CellContext<T>) => CellSpan | undefinedundefinedrowspan 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 stringundefinedClass on the leaf header cell.

Sort, filter and aggregate

Defaults cover numbers, dates and text; each can be replaced per column.

NameTypeDefaultDescription
# sortable booleantrueHeader sorting; empty values sort last in both directions.
# compare (a: unknown, b: unknown, aRow: T, bRow: T) => numbernumbers, dates, then a locale collatorCustom comparator; the sort direction is applied to its result.
# sortDescFirst booleanfalseThe first activation sorts descending: descending, ascending, none.
# sortKeys string[]undefinedFurther sort ids that count as this column sorted (aria-sort, the icon), for a headerCell that sorts by several fields.
# filterable booleantrueGets a filter input when filters is on.
# filter (value: unknown, filter: Filter, row: T) => booleanmatchesFilterCustom predicate instead of the operator matcher.
# searchable booleantrueIncluded in the search.
# aggregate 'sum' | 'mean' | 'min' | 'max' | 'count' | ((values: unknown[]) => unknown)undefinedAggregate used by groupBy() and summarize().

Editing

The renderer parses and validates; the application commits.

NameTypeDefaultDescription
# editable boolean | ((row: T) => boolean)falseCell 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) => unknownparseEditConverts the editor text to the domain value.
# validate (value: unknown, row: T) => string | undefinedundefinedReturns 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.

NameTypeDefaultDescription
# sorting Sort[][]Ordered sort rules; the array order is the multi-sort precedence.
# filters Filter[][]Column filters with operator and value.
string''The search text; terms are split on whitespace.
# pageIndex number0Zero-based page.
# pageSize number20Rows per page; 0 shows every row.
# selection RowId[][]Selected row ids.
# expanded RowId[][]Expanded details, tree and group row ids.
# hidden 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.

NameTypeDefaultDescription
# 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.

NameTypeDefaultDescription
# 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.

NameTypeDefaultDescription
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.

own renderer
<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 engine
// 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
core entry, no Svelte
// 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.

NameTypeDefaultDescription
# data requiredMaybeGetter<readonly T[]>–Rows.
# columns requiredMaybeGetter<readonly Column<T>[]>–Column schema.
# getRowId required(row: T) => RowId–Row identity.
# getSubRows (row: T) => readonly T[] | undefinedundefinedTree 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) => voidundefinedReports every change, owned or controlled.
# getRowModel (context: ModelContext<T>) => RowModel<T>createRowModelCustom row model.
# manual MaybeGetter<boolean | undefined>falseManual mode.
# rowCount MaybeGetter<number | undefined>undefinedManual row count.
# pageCount MaybeGetter<number | undefined>undefinedManual page count when the total is unknown.
# searchText (row: T) => stringundefinedSearchable text of a row instead of the searchable columns.
# locale MaybeGetter<string | undefined>undefinedCollator locale.
# pagination MaybeGetter<boolean | undefined>truefalse 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() => trueSelection 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.

NameTypeDefaultDescription
# 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.

NameTypeDefaultDescription
# @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 pointgzipbrotliBudget (gzip)
@svelte-lean/table/grouping2148 B1963 B2400 B
@svelte-lean/table/pivot526 B470 B600 B
@svelte-lean/table/editing707 B617 B800 B
@svelte-lean/table/clipboard895 B817 B1000 B
@svelte-lean/table/export741 B646 B850 B
@svelte-lean/table/server369 B310 B425 B
@svelte-lean/table/state796 B711 B900 B
@svelte-lean/table/style.css6665 B5704 B7300 B