# Selection + copy/paste

Row + cell-range selection with TSV clipboard round-trip.

Uses `@svgrid/grid` (MIT). Category: Selection & Clipboard. Live demo: https://svgrid.com/demos/04-selection-copy-paste/

Install: `npm i @svgrid/grid` (Svelte 5).

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

- [Cell text selection](https://svgrid.com/docs/help/cells/cell-text-selection.md)
- [Getting values](https://svgrid.com/docs/help/cells/getting-values.md)
- [Recipes / Cookbook](https://svgrid.com/docs/help/recipes.md)
- [Accessing rows](https://svgrid.com/docs/help/rows/accessing-rows.md)
- [Shadow DOM: `<sv-grid-shadow>`](https://svgrid.com/docs/help/web-components/shadow-dom.md)

## Related articles

- [Copy a Cell Range to the Clipboard in SvGrid](https://svgrid.com/blog/copy-cell-range-to-clipboard/) - 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.
- [Row Selection and Clipboard Copy-Paste in SvGrid](https://svgrid.com/blog/row-selection-and-copy-paste/) - How checkbox row selection, cell range copy, and Excel-compatible clipboard output work together in SvGrid - with a full working example.

## Source code (04-selection-copy-paste.svelte)

```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>
```

[View this example on GitHub](https://github.com/sv-grid/sv-grid/blob/main/examples/src/demos/04-selection-copy-paste.svelte)
