Selection + copy/paste
Row + cell-range selection with TSV clipboard round-trip.
A live, editable Svelte 5 data grid example from the SvGrid gallery (Selection & Clipboard). See the SvGrid documentation for the full API.
About this example
Row and cell-range selection in the Svelte 5 data grid with a built-in TSV clipboard. selectionMode='both' gives row checkboxes and rectangular cell selection at once; Ctrl or Cmd+C copies the active rectangle as tab-separated text and Ctrl+V pastes TSV back into the same range, so data moves straight into and out of a spreadsheet. A hand-rolled footer summarises the current selection from onRowSelectionChange.
Row checkboxes (selectionMode='both') + cell-range selection. Built-in clipboard:
- Ctrl/Cmd+C copies the active rectangular selection as TSV
- Ctrl/Cmd+V pastes TSV from the clipboard into the same range The selection summary footer below the grid is rolled by hand - the grid exposes the selected-cell rectangle via the active-cell state.
Imports, features and API used
Imports: @svgrid/grid, ../shared/seed
Table features registered: rowSortingFeature, rowSelectionFeature
Columns: firstName (First name), lastName (Last name), department (Department), country (Country), age (Age), salary (Salary), performance (Performance)
Frequently asked questions
How do I copy cells out of the grid?
Select a rectangle with click and drag or Shift and the arrow keys, then press Ctrl or Cmd+C. The grid writes the range to the clipboard as TSV, which spreadsheets paste as cells. With contextMenu on, the right-click menu offers copy, cut, paste and clear as well.
Does paste work from a spreadsheet?
Yes. Copy cells in a spreadsheet, select the target cell in the grid and press Ctrl or Cmd+V. The TSV is split into cells starting at the active cell; inline editing is off here so the demo is read-only, turn it on to write.
How do I get the selected rows?
onRowSelectionChange is called with the selection state and the selected row objects; the demo stores the rows and renders the footer from them.
Related documentation
Related articles
- Row Selection and Clipboard Copy-Paste in SvGrid - How checkbox row selection, cell range copy, and Excel-compatible clipboard output work together in SvGrid - with a full working example.
- Copy a Cell Range to the Clipboard in SvGrid - How SvGrid copies selected cell ranges as tab-separated values that paste cleanly into Excel and Google Sheets, plus headers, programmatic copy, and format control.
- Paste from Excel into a Svelte Data Grid - How to wire up clipboard paste so users can drop a copied Excel or Google Sheets block directly into SvGrid - TSV parsing, type coercion, validation, and row growth all covered.
Source code (04-selection-copy-paste.svelte)
<!-- Documented in: docs/help/recipes.md -->
<script lang="ts">
/**
* 04. Selection + copy/paste
* --------------------------
* Row checkboxes (selectionMode='both') + cell-range selection.
* Built-in clipboard:
* - Ctrl/Cmd+C copies the active rectangular selection as TSV
* - Ctrl/Cmd+V pastes TSV from the clipboard into the same range
* The selection summary footer below the grid is rolled by hand -
* the grid exposes the selected-cell rectangle via the active-cell state.
*/
import {
SvGrid,
tableFeatures,
rowSortingFeature,
rowSelectionFeature,
type GridColumns,
type SvGridApi,
} from '@svgrid/grid'
import { makePeople, type Person } from '../shared/seed'
const features = tableFeatures({
rowSortingFeature,
rowSelectionFeature,
})
const rows = makePeople(80)
const columns: GridColumns<Person> = [
{ field: 'firstName', header: 'First name', editorType: 'text' },
{ field: 'lastName', header: 'Last name', editorType: 'text' },
{ field: 'department', header: 'Department', editorType: 'text' },
{ field: 'country', header: 'Country', editorType: 'text' },
{
field: 'age',
header: 'Age',
editorType: 'number',
},
{
field: 'salary',
header: 'Salary',
editorType: 'number',
format: { type: 'currency', currency: 'USD', options: { maximumFractionDigits: 0 } },
},
{
field: 'performance',
header: 'Performance',
editorType: 'number',
},
]
let api = $state<SvGridApi<typeof features, Person> | null>(null)
let selectedRows = $state<Person[]>([])
const stats = $derived.by(() => {
let sumSalary = 0
let perfTotal = 0
for (const row of selectedRows) {
sumSalary += row.salary
perfTotal += row.performance
}
return {
count: selectedRows.length,
sumSalary,
avgPerf: selectedRows.length ? Math.round(perfTotal / selectedRows.length) : 0,
}
})
const currency = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 })
</script>
<section class="flex flex-col flex-1 min-h-0 gap-3">
<div class="flex flex-wrap items-center gap-3 text-sm shrink-0">
<span class="text-slate-600 dark:text-slate-300">
Tick the row checkboxes, or click + drag cells to make a range selection.
<kbd>Ctrl/Cmd+C</kbd> copies as TSV.
</span>
</div>
<div class="flex-1 min-h-0">
<SvGrid responsive={true}
columnResize
data={rows}
columns={columns}
features={features}
filterMode="menu"
selectionMode="both"
enableInlineEditing={false}
enableCellSelection={true}
contextMenu={true}
rowHeight={36}
containerHeight="100%"
fitColumns={true}
onApiReady={(next) => (api = next)}
onRowSelectionChange={(_, rows) => (selectedRows = rows)}
/>
</div>
<footer class="sel-summary rounded border px-4 py-2 text-sm flex flex-wrap items-center gap-6 shrink-0">
<span><strong>{stats.count}</strong> rows selected</span>
<span>Σ Salary: <strong>{currency.format(stats.sumSalary)}</strong></span>
<span>⌀ Performance: <strong>{stats.avgPerf}</strong></span>
</footer>
</section>
<style>
/* Theme tokens rather than fixed slate utilities, so the summary bar tracks
whichever preset the gallery is running. */
.sel-summary {
background: var(--sg-bg-subtle, var(--sg-header-bg, #f8fafc));
border-color: var(--sg-border, #e2e8f0);
color: var(--sg-fg, #0f172a);
}
</style>More Selection & Clipboard examples
- Bulk-action bar - selectionBar floats a bar over the grid while rows are selected - the count, your actions, an overflow menu, and Clear - the pattern an issue tracker uses for bulk edit. position pins it to the bottom (default) or top edge. Actions receive the selected rows in display order, and hidden / disabled re-check against the live selection so buttons appear and grey out as the set changes.
- Selection API + events - Drive cell selection with api.selectCells / api.getSelected; subscribe to changes via onCellSelectionChange. Live SUM/AVG/MIN/MAX panel + event log + copy-as-TSV.
- Status bar (range aggregates) - Excel-style bar under the grid with live aggregates of the selected cell range: Count, Sum, Avg, Min, Max. Enable with statusBar + enableCellSelection, then drag a rectangle across numeric cells. Choose the aggregate set via statusBar={ aggregates }.
- Range selection (Excel-style) - Drag any rectangle of cells, or Ctrl/Cmd+drag to add MORE ranges - all stay highlighted and copy together. Toolbar issues common ranges; live SUM/AVG/MIN/MAX/COUNT status bar; copy as TSV.
- Bulk actions toolbar - Select rows → sticky action bar with Mark / Delete / Copy as TSV. The Gmail / Linear pattern.