# Selection

> Single and multiple selection, disabled rows, select-all scope, controlled selection and keyboard.

Page: https://svelte-lean.vercel.app/table/selection · Markdown: https://svelte-lean.vercel.app/table/selection.md · Index: https://svelte-lean.vercel.app/llms.txt

Selection is a list of row ids in the state, rendered as native checkboxes or radios. Which rows a select-all covers, which rows can be selected at all, and what the selection means are the application's decisions.

- **Source**: core.ts (toggleSelection)table.svelte.ts (selectable, selectAll)data-table.svelte
- **Tests**: core.test.tsdata-table.dom.test.tstable.test.tstable.spec.ts (browser)
- **Fixture**: fixtures/table-basic
- **Benchmark**: table.bench.spec.ts

The controls are native inputs, so the browser provides focus, Space to toggle, the checked state and the radio group semantics. Multiple selection renders one checkbox per row and a header checkbox with an indeterminate state; single selection renders radios that share the name `<id>-selection`, which is why `selection="single"` needs the `id` prop. Each row control is labelled with the `selectRow` message and the row id; the selected count is announced through a polite live region.

`selectionScope` decides what the header checkbox and `selectAll()` act on: the rows on the current page, or every filtered row across pages. Rows that fail `isRowSelectable` render a disabled control and are excluded from both. Ids stay in the state when a row scrolls off the page or is filtered out, so a selection survives navigation and search.

## Multiple selection

Single or multiple selection, disabled records and page or filtered select-all scope.

Selection.svelte

```svelte
<script lang="ts">
	import { DataTable, defaultState, type TableState } from '@svelte-lean/table';
	import '@svelte-lean/styles/radio-group.css';
	import { memberColumns, pick } from '$lib/demo/columns';
	import { people } from '$lib/demo/data';

	const data = people(12);
	const columns = pick(memberColumns(), ['name', 'status', 'team', 'revenue']);
	let mode = $state<'multiple' | 'single'>('multiple');
	let scope = $state<'page' | 'filtered'>('filtered');
	let tableState = $state<Partial<TableState>>(defaultState({ pageSize: 6 }));
</script>

<fieldset data-slean="radio-group">
	<legend>Mode</legend>
	<label><input type="radio" name="mode" value="multiple" bind:group={mode} /> multiple</label>
	<label><input type="radio" name="mode" value="single" bind:group={mode} /> single</label>
</fieldset>
<fieldset data-slean="radio-group">
	<legend>Select-all scope</legend>
	<label><input type="radio" name="scope" value="page" bind:group={scope} /> page</label>
	<label><input type="radio" name="scope" value="filtered" bind:group={scope} /> filtered</label>
</fieldset>
<button type="button" data-slean="button" data-variant="outlined" data-color="neutral" onclick={() => (tableState = { ...tableState, selection: [] })}>Clear</button>

<!-- id names the radio group in single mode: <id>-selection. Invited members cannot be selected. -->
<DataTable
	id="members"
	{data}
	{columns}
	getRowId={(row) => row.id}
	bind:state={tableState}
	selection={mode}
	selectionScope={scope}
	isRowSelectable={(row) => row.status !== 'Invited'}
	searchable
	label="Members"
/>

<pre>{JSON.stringify(tableState.selection)}</pre>
```

Mode and scope are switched by two native radio groups (Tier 0 primitives); Clear is the Button primitive writing an empty list into the state. Invited members cannot be selected. With the filtered scope, search for a team and use the header checkbox: every matching row across pages is selected.

- Switching to single mode keeps the list; the next selection replaces it with one id.
- The pre element prints `state.selection`; nothing else about the rows changes.

## Keyboard

| Key | When | Result |
| --- | --- | --- |
| `Tab` | in the body | Moves through the row controls in order. |
| `Space` | on a checkbox | Toggles the row; on the header checkbox, selects or clears the scope. |
| `ArrowUp`/`ArrowDown` | on a radio | Moves the selection to the previous or next selectable row (native radio group). |

## API

| Name | Of | Type | Default | Description |
| --- | --- | --- | --- | --- |
| [`selection`](https://svelte-lean.vercel.app/table/api.md#prop-selection) | Controls | `false \| 'single' \| 'multiple'` | `false` | Adds a radio or checkbox column. 'single' needs the id prop for the radio group name. |
| [`selectionScope`](https://svelte-lean.vercel.app/table/api.md#prop-selectionscope) | Controls | `'page' \| 'filtered'` | `'page'` | Rows that select-all and the header checkbox consider: the current page or every filtered row. |
| [`isRowSelectable`](https://svelte-lean.vercel.app/table/api.md#prop-isrowselectable) | Controls | `(row: T) => boolean` | `() => true` | Disables the selection control of rows that fail the rule. |
| [`hideSelectAll`](https://svelte-lean.vercel.app/table/api.md#prop-hideselectall) | Controls | `boolean` | `false` | Hides the header checkbox of multiple selection; the header cell keeps the selectionHeader text for assistive technology. |
| [`id`](https://svelte-lean.vercel.app/table/api.md#prop-id) | Data and identity | `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. |
| [`selection`](https://svelte-lean.vercel.app/table/api.md#state-selection) | TableState | `RowId[]` | `[]` | Selected row ids. |
| [`selectAll`](https://svelte-lean.vercel.app/table/api.md#message-selectall) | TableMessages | `string` | `'Select all eligible rows'` | Label of the header checkbox. |
| [`selectRow`](https://svelte-lean.vercel.app/table/api.md#message-selectrow) | TableMessages | `string` | `'Select row'` | Prefix of each row control label (followed by the row id). |
| [`selected`](https://svelte-lean.vercel.app/table/api.md#message-selected) | TableMessages | `(count: number) => string` | `` (count) => `${count} selected` `` | The live selection count in the toolbar. |
| [`toggleSelection`](https://svelte-lean.vercel.app/table/api.md#table-toggleselection) | Table | `(id: RowId, selected?: boolean) => void` | – | Toggles one row; in 'single' mode the row replaces the selection. |
| [`selectAll`](https://svelte-lean.vercel.app/table/api.md#table-selectall) | Table | `(ids?: readonly RowId[], selected?: boolean) => void` | – | Selects or clears many rows; defaults to selectable and to select unless all are selected. |
| [`selectable`](https://svelte-lean.vercel.app/table/api.md#table-selectable) | Table | `RowId[]` | – | Ids a selection control may act on within selectionScope. |
| [`toggleSelection`](https://svelte-lean.vercel.app/table/api.md#export-toggleselection) | Root entry | `(current: RowId[], eligible: RowId[], select?: boolean) => RowId[]` | – | Adds or removes the eligible ids; without select it selects unless all are selected. |

## Related

- [Filtering](https://svelte-lean.vercel.app/table/filtering.md)
- [Clipboard & Export](https://svelte-lean.vercel.app/table/clipboard-export.md)
- [Accessibility](https://svelte-lean.vercel.app/table/accessibility.md)
