gridcn1.3.0

Clipboard

Native copy/cut/paste with spreadsheet-compatible TSV and HTML formats.

Clipboard support uses native browser copy, cut, and paste events.

Name
Email
Age
Active
Role
Joined
Score
Silent Tiger
user0@example.com
63
Viewer
Jul 5, 2023
4
Quick Lion
user1@example.com
67
Viewer
Apr 3, 2021
89
Swift Wolf
user2@example.com
40
Manager
Oct 27, 2020
80
Eager Lion
user3@example.com
50
User
Oct 8, 2022
51
Eager Tiger
user4@example.com
45
Viewer
Oct 7, 2024
83
Silent Wolf
user5@example.com
24
User
Jun 1, 2020
15
Swift Lion
user6@example.com
34
Viewer
Oct 18, 2022
62
Eager Lion
user7@example.com
21
Manager
Oct 7, 2021
42
Eager Eagle
user8@example.com
25
Editor
Jun 11, 2024
35
Bold Tiger
user9@example.com
19
Editor
Mar 20, 2024
52
Bold Bear
user10@example.com
42
User
Apr 16, 2020
28
Swift Wolf
user11@example.com
47
Manager
Aug 4, 2022
9

Copy

Copy, cut, and paste use the native browser clipboard events, so no clipboard-write permission prompt appears for copy or cut. Selecting a range and pressing Ctrl/Cmd+C writes two formats to the system clipboard:

  • text/plain: tab-separated values (TSV), one row per line. A cell that contains a tab, newline, or quote is quoted, with the quote doubled internally, matching the TSV quoting of Excel and Sheets.
  • text/html: an HTML <table> with the raw value of each cell stashed in data-* attributes. So a paste back into gridcn recovers the exact typed values, not only their display text, while a paste into a real spreadsheet app still gets a normal table.

toText, not toDisplayText

The copied text of each cell comes from the toText of its cell type. Display formatting, such as a locale date string, never leaks into the clipboard.

Formula sanitizing

Excel, LibreOffice, and Sheets run a pasted cell that starts with =, +, -, @, TAB, or CR as a formula. To paste such cells into those apps as text, set sanitizeClipboardFormulas:

<DataGrid {...grid} columns={columns} sanitizeClipboardFormulas />

When the prop is set, Ctrl/Cmd+C, Ctrl/Cmd+X, and the context menu's Copy and Cut add a single quote in front of each formula-like cell. The quote goes into both the text/plain and the text/html payload. Plain numbers such as -5 and +5 stay unchanged. A paste back into gridcn restores the raw value. In a browser that lacks or denies the async Clipboard API, the copy falls back to a plain-text-only write. A paste back from that copy keeps the quote.

PropTypeDefault
sanitizeClipboardFormulasbooleanfalse

DataGridProvider takes the same prop. A custom copy UI on useDataGridClipboard follows the prop; its sanitizeFormulas option overrides it.

Paste

Pasting parses HTML first, when the clipboard has a text/html payload, for example when you paste from another gridcn instance, Excel, Sheets, or Numbers. It falls back to a quoted-TSV parser for plain-text sources, handling tabs, newlines, and quotes inside quoted cells.

Paste semantics:

  • Anchored expand: pasting an N×M block starting at the active cell overwrites that N×M region.
  • Single-row tiling down a selection: pasting one row into a taller selected range repeats it down every row.
  • Pasted text runs through the fromText of each target cell type, then through validate if present. An invalid cell in the pasted block is rejected the same way as a manual edit.
  • readOnly cells are skipped. Both the column's static readOnly and its per-row function form apply.
  • One paste produces one onDataChange call with a single operations batch, regardless of how many cells it touched.

Overrides

Three DataGridProvider props let you intercept the pipeline without forking it.

import type { ViewCoord, ColumnDef } from "@/components/data-grid/data-grid";

// Module scope (or useCallback) so identity stays stable across renders.
function processCellForClipboard(value: unknown, { row, column }: { row: Row; column: ColumnDef<Row, unknown> }) {
  // override what gets copied for this cell; falls back to the cell type's toText
  return String(value);
}

function processCellFromClipboard(text: string, { row, column }: { row: Row; column: ColumnDef<Row, unknown> }) {
  // override how pasted text becomes a value; falls back to the cell type's fromText
  return text.trim();
}

function processPaste({ cells, target }: { cells: string[][]; target: ViewCoord }) {
  // runs after tiling and before the paste cap; return false to veto entirely
  return cells;
}

<DataGridProvider
  processCellForClipboard={processCellForClipboard}
  processCellFromClipboard={processCellFromClipboard}
  processPaste={processPaste}
  {...grid}
  columns={columns}
/>

processPaste runs on the paste grid after single-row tiling and before the paste cap. target is the top-left cell the paste applies to.

Context menu paste

The context menu add-on adds a Paste item to the cell-surface menu. It needs the clipboard-read permission and degrades to a disabled state with a "press Ctrl+V" hint when the browser withholds it.

Known limitations

Copy and paste are capped at 200,000 cells

A copy above MAX_COPY_CELLS (200,000 cells) writes only the rows that fit; a paste above MAX_PASTE_CELLS (200,000 cells) applies only the cells that fit. A truncated copy or paste warns once in the browser console in development.

Real-world clipboard HTML is messy

Clipboard HTML from Excel, Google Sheets, and Apple Numbers is often inconsistent, with merged-cell artifacts and inline styles that masquerade as structure. The parser is defensive but not exhaustive. If a paste does not round-trip correctly from a specific app, use processPaste or processCellFromClipboard to patch it for your use case.

An async schema holds the paste

A paste against a column with an async Standard Schema validate waits for the schema and commits the accepted cells as one batch. A rejected cell drops from the batch, and the paste reports it through onValidationError when you pass one. The full contract, shared by fill and import, is on Editing & cell types: async schemas. For a large paste against a remote check, processPaste above validates the whole block in one request instead.

On this page