React Data Grid provides built-in support for copying cell values and pasting content back into editable cells using standard keyboard shortcuts.
Basic Usage
Copy and paste work automatically when cells are editable:
import { DataGrid , type Column , renderTextEditor } from 'react-data-grid' ;
import { useState } from 'react' ;
interface Row {
id : number ;
name : string ;
email : string ;
phone : string ;
}
const columns : Column < Row >[] = [
{ key: 'id' , name: 'ID' },
{
key: 'name' ,
name: 'Name' ,
renderEditCell: renderTextEditor
},
{
key: 'email' ,
name: 'Email' ,
renderEditCell: renderTextEditor
},
{
key: 'phone' ,
name: 'Phone' ,
renderEditCell: renderTextEditor
}
];
function MyGrid () {
const [ rows , setRows ] = useState < Row []>( initialRows );
return (
< DataGrid
columns = { columns }
rows = { rows }
onRowsChange = { setRows }
/>
);
}
Keyboard : Ctrl+C (Windows/Linux) or Cmd+C (Mac)
Select a cell
Press copy shortcut
Cell value is copied to clipboard
Keyboard : Ctrl+V (Windows/Linux) or Cmd+V (Mac)
Select an editable cell
Press paste shortcut
Clipboard content is pasted into cell
onRowsChange is called with updated data
Paste only works on editable cells. Non-editable cells ignore paste events.
Copy Behavior
Default Copy
By default, the grid copies the raw cell value:
// From src/DataGrid.tsx
function handleCellCopy ( event : CellClipboardEvent ) {
if ( ! selectedCellIsWithinViewportBounds ) return ;
const { idx , rowIdx } = selectedPosition ;
onCellCopy ?.({ row: rows [ rowIdx ], column: columns [ idx ] }, event );
}
The browser’s default copy behavior is used, which typically copies row[column.key] as text.
Custom Copy Handler
Customize what gets copied:
import type { CellCopyArgs , CellClipboardEvent } from 'react-data-grid' ;
function handleCellCopy (
{ row , column } : CellCopyArgs < Row >,
event : CellClipboardEvent
) {
const value = row [ column . key as keyof Row ];
// Custom formatting
let copyText : string ;
if ( column . key === 'price' ) {
copyText = `$ ${ value . toFixed ( 2 ) } ` ;
} else if ( column . key === 'date' ) {
copyText = new Date ( value ). toLocaleDateString ();
} else {
copyText = String ( value );
}
// Write to clipboard
event . clipboardData . setData ( 'text/plain' , copyText );
event . preventDefault ();
}
< DataGrid
columns = { columns }
rows = { rows }
onCellCopy = { handleCellCopy }
/>
Paste Behavior
Default Paste
Pasting updates the cell with clipboard content:
// From src/DataGrid.tsx
function handleCellPaste ( event : CellClipboardEvent ) {
if ( ! onCellPaste || ! onRowsChange || ! isCellEditable ( selectedPosition )) {
return ;
}
const { idx , rowIdx } = selectedPosition ;
const column = columns [ idx ];
const updatedRow = onCellPaste ({ row: rows [ rowIdx ], column }, event );
updateRow ( column , rowIdx , updatedRow );
}
By default, paste is not enabled unless you provide an onCellPaste handler.
Custom Paste Handler
Implement paste with data transformation:
import type { CellPasteArgs , CellClipboardEvent } from 'react-data-grid' ;
function handleCellPaste (
{ row , column } : CellPasteArgs < Row >,
event : CellClipboardEvent
) : Row {
const pastedText = event . clipboardData . getData ( 'text/plain' );
const columnKey = column . key as keyof Row ;
// Parse and validate based on column type
let parsedValue : any ;
if ( column . key === 'email' ) {
// Validate email
const emailRegex = / ^ [ ^ \s@ ] + @ [ ^ \s@ ] + \. [ ^ \s@ ] + $ / ;
parsedValue = emailRegex . test ( pastedText ) ? pastedText : row . email ;
} else if ( column . key === 'age' ) {
// Parse number
const num = parseInt ( pastedText , 10 );
parsedValue = ! isNaN ( num ) && num > 0 ? num : row . age ;
} else if ( column . key === 'price' ) {
// Parse currency
const cleaned = pastedText . replace ( / [ $, ] / g , '' );
const num = parseFloat ( cleaned );
parsedValue = ! isNaN ( num ) ? num : row . price ;
} else {
parsedValue = pastedText ;
}
return { ... row , [columnKey]: parsedValue };
}
< DataGrid
columns = { columns }
rows = { rows }
onRowsChange = { setRows }
onCellPaste = { handleCellPaste }
/>
Always return a new row object from onCellPaste. The grid will automatically call onRowsChange with the updated row.
Copy/Paste with Validation
Validate pasted data before updating:
function handleCellPaste (
{ row , column } : CellPasteArgs < Row >,
event : CellClipboardEvent
) : Row {
const pastedText = event . clipboardData . getData ( 'text/plain' ). trim ();
const columnKey = column . key as keyof Row ;
// Validation rules
const validators : Record < string , ( value : string ) => boolean > = {
email : ( v ) => / ^ [ ^ \s@ ] + @ [ ^ \s@ ] + \. [ ^ \s@ ] + $ / . test ( v ),
phone : ( v ) => / ^ \d {10} $ / . test ( v . replace ( / \D / g , '' )),
url : ( v ) => / ^ https ? : \/\/ / . test ( v ),
zipCode : ( v ) => / ^ \d {5} $ / . test ( v )
};
const validator = validators [ column . key ];
if ( validator && ! validator ( pastedText )) {
// Invalid: show error or keep original value
console . warn ( `Invalid value for ${ column . key } : ${ pastedText } ` );
return row ; // Don't update
}
// Valid: update the row
return { ... row , [columnKey]: pastedText };
}
Validation with User Feedback
Show validation errors to users: function MyGrid () {
const [ rows , setRows ] = useState < Row []>( initialRows );
const [ error , setError ] = useState < string | null >( null );
function handleCellPaste (
{ row , column } : CellPasteArgs < Row >,
event : CellClipboardEvent
) : Row {
const pastedText = event . clipboardData . getData ( 'text/plain' ). trim ();
if ( column . key === 'email' ) {
if ( ! / ^ [ ^ \s@ ] + @ [ ^ \s@ ] + \. [ ^ \s@ ] + $ / . test ( pastedText )) {
setError ( 'Invalid email format' );
setTimeout (() => setError ( null ), 3000 );
return row ;
}
}
setError ( null );
return { ... row , [column.key]: pastedText };
}
return (
<>
{ error && (
< div style = { { padding: '8px' , backgroundColor: '#ffebee' , color: '#c62828' } } >
{ error }
</ div >
) }
< DataGrid
columns = { columns }
rows = { rows }
onRowsChange = { setRows }
onCellPaste = { handleCellPaste }
/>
</>
);
}
Transform data during copy/paste:
function MyGrid () {
const [ rows , setRows ] = useState < Row []>( initialRows );
function handleCellCopy (
{ row , column } : CellCopyArgs < Row >,
event : CellClipboardEvent
) {
const value = row [ column . key as keyof Row ];
let copyText = String ( value );
// Transform for copying
if ( column . key === 'status' ) {
// Copy status code instead of display text
const statusCodes : Record < string , string > = {
'Active' : 'A' ,
'Inactive' : 'I' ,
'Pending' : 'P'
};
copyText = statusCodes [ value ] || value ;
}
event . clipboardData . setData ( 'text/plain' , copyText );
event . preventDefault ();
}
function handleCellPaste (
{ row , column } : CellPasteArgs < Row >,
event : CellClipboardEvent
) : Row {
const pastedText = event . clipboardData . getData ( 'text/plain' ). trim ();
let value = pastedText ;
// Transform for pasting
if ( column . key === 'status' ) {
// Convert status code to display text
const statusTexts : Record < string , string > = {
'A' : 'Active' ,
'I' : 'Inactive' ,
'P' : 'Pending'
};
value = statusTexts [ pastedText . toUpperCase ()] || pastedText ;
}
return { ... row , [column.key]: value };
}
return (
< DataGrid
columns = { columns }
rows = { rows }
onRowsChange = { setRows }
onCellCopy = { handleCellCopy }
onCellPaste = { handleCellPaste }
/>
);
}
Preventing Copy/Paste
Disable for Specific Columns
Prevent copy/paste on sensitive columns:
function handleCellCopy (
{ column } : CellCopyArgs < Row >,
event : CellClipboardEvent
) {
// Prevent copying password fields
if ( column . key === 'password' ) {
event . preventDefault ();
return ;
}
// Default behavior for other columns
}
function handleCellPaste (
{ row , column } : CellPasteArgs < Row >,
event : CellClipboardEvent
) : Row {
// Prevent pasting into ID field
if ( column . key === 'id' ) {
return row ; // Don't update
}
const pastedText = event . clipboardData . getData ( 'text/plain' );
return { ... row , [column.key]: pastedText };
}
Disable Globally
Don’t provide handlers to disable copy/paste:
// Copy works (browser default)
// Paste disabled (no handler)
< DataGrid
columns = { columns }
rows = { rows }
onRowsChange = { setRows }
// onCellCopy not provided
// onCellPaste not provided
/>
Limitations
Current Limitations :
Single Cell Only : Cannot copy/paste multiple cells or ranges
No Drag Selection : Cannot select and copy multiple cells at once
Group Rows : Copy/paste disabled on TreeDataGrid group rows
Summary Rows : Copy/paste not available on summary rows
Read-Only Cells : Paste only works on editable cells
From src/TreeDataGrid.tsx: // Copy is prevented on group rows
function handleCellCopy (
{ row , column } : CellCopyArgs < NoInfer < R >, NoInfer < SR >>,
event : CellClipboardEvent
) {
if ( ! isGroupRow ( row )) {
rawOnCellCopy ?.({ row , column }, event );
}
}
// Paste returns unchanged row for group rows
function handleCellPaste (
{ row , column } : CellPasteArgs < NoInfer < R >, NoInfer < SR >>,
event : CellClipboardEvent
) {
return isGroupRow ( row ) ? row : rawOnCellPaste ! ({ row , column }, event );
}
API Reference
DataGrid Props
onCellCopy
onCellCopy ?: ( args : CellCopyArgs < R , SR >, event : CellClipboardEvent ) => void
Description : Callback triggered when a cell’s content is copied.
Parameters :
args: Object containing row and column information
event: React clipboard event
onCellPaste
onCellPaste ?: ( args : CellPasteArgs < R , SR >, event : CellClipboardEvent ) => R
Description : Callback triggered when content is pasted into a cell.
Parameters :
args: Object containing row and column information
event: React clipboard event
Returns : Updated row object. The grid will call onRowsChange with it.
Types
CellCopyArgs
interface CellCopyArgs < TRow , TSummaryRow = unknown > {
column : CalculatedColumn < TRow , TSummaryRow >;
row : TRow ;
}
CellPasteArgs
interface CellPasteArgs < TRow , TSummaryRow = unknown > {
column : CalculatedColumn < TRow , TSummaryRow >;
row : TRow ;
}
CellClipboardEvent
type CellClipboardEvent = React . ClipboardEvent < HTMLDivElement >;
Standard React clipboard event with clipboardData property.