Skip to content

Copy, Cut & Paste

Spreadsheet’s ClipboardManager provides full clipboard support. Selected cells can be copied (Ctrl+C), cut (Ctrl+X), or pasted (Ctrl+V). Data is written in two formats simultaneously: text/plain as tab-separated values (TSV) and text/html as an HTML table. This dual-format approach enables direct interop with Excel and Google Sheets.

Live Demo
Select cells, copy (Ctrl+C), cut (Ctrl+X), click another cell, paste (Ctrl+V). Changes appear in the log below.
Select cells and use Ctrl+C / Ctrl+X / Ctrl+V
View source code
ClipboardDemo.tsx
import { useState } from 'react';
import { Spreadsheet } from '@witqq/spreadsheet-react';
import type { CellChangeEvent } from '@witqq/spreadsheet';
import { DemoWrapper } from './DemoWrapper';
import { generateEmployees, employeeColumns } from './generate-data';
import { useSiteTheme } from './useSiteTheme';
const data = generateEmployees(50);
export function ClipboardDemo() {
const { witTheme } = useSiteTheme();
const [log, setLog] = useState<string[]>(['Select cells and use Ctrl+C / Ctrl+X / Ctrl+V']);
const handleCellChange = (event: CellChangeEvent) => {
setLog((prev) => [`Cell (${event.row}, ${event.col}) → "${event.value}"`, ...prev.slice(0, 9)]);
};
return (
<DemoWrapper
title="Live Demo"
description="Select cells, copy (Ctrl+C), cut (Ctrl+X), click another cell, paste (Ctrl+V). Changes appear in the log below."
height={440}
>
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<div style={{ flex: 1 }}>
<Spreadsheet
theme={witTheme}
columns={employeeColumns}
data={data}
showRowNumbers
editable
onCellChange={handleCellChange}
style={{ width: '100%', height: '100%' }}
/>
</div>
<div
style={{
padding: '0.5rem 0.75rem',
fontSize: '0.75rem',
color: '#64748b',
borderTop: '1px solid #e2e8f0',
flexShrink: 0,
maxHeight: 60,
overflow: 'auto',
}}
>
{log.map((entry, i) => (
<div key={i}>{entry}</div>
))}
</div>
</div>
</DemoWrapper>
);
}

Cut operations clear the source cells using an undoable BatchCellEditCommand, so Ctrl+Z restores the original values. Paste clips at grid boundaries — if the pasted data exceeds the grid dimensions, excess rows and columns are silently dropped.

Clipboard works automatically on any editable table. No additional configuration is needed:

import { Spreadsheet } from '@witqq/spreadsheet-react';
function App() {
return (
<Spreadsheet
columns={columns}
data={data}
editable={true}
/>
);
}

Select a range, press Ctrl+C to copy, click a destination cell, and press Ctrl+V to paste.

Subscribe to clipboard events via the engine’s EventBus:

import { useRef, useEffect } from 'react';
import { Spreadsheet } from '@witqq/spreadsheet-react';
import type { SpreadsheetRef } from '@witqq/spreadsheet-react';
function App() {
const ref = useRef<SpreadsheetRef>(null);
useEffect(() => {
const engine = ref.current?.getInstance();
if (!engine) return;
engine.on('clipboardCopy', (event) => {
console.log('Copied cells');
});
engine.on('clipboardPaste', (event) => {
console.log('Pasted cells');
});
}, []);
return (
<Spreadsheet ref={ref} columns={columns} data={data} editable={true} />
);
}
FormatMIME TypeContent
TSVtext/plainTab-separated values, newlines between rows
HTMLtext/html<table> with <tr> and <td> elements

When pasting from external sources, Spreadsheet reads text/html first for richer structure (Excel/Google Sheets format). If unavailable, it falls back to parsing text/plain (TSV).

The clipboard module exports standalone serialization functions:

import { serializeToTSV, serializeToHTML, parseTSV, parseHTML } from '@witqq/spreadsheet';
// Serialize cell data to clipboard formats
const tsv: string = serializeToTSV(cells); // Tab-separated values
const html: string = serializeToHTML(cells); // HTML <table>
// Parse clipboard data back to cell values
const fromTSV: CellValue[][] = parseTSV(tsvString);
const fromHTML: CellValue[][] | null = parseHTML(htmlString);