---
title: "Clipboard"
enterprise: true
framework: react
version: "36.1.0"
---

# Clipboard

You can copy and paste items to and from the grid using the system clipboard.

## How to copy

Copying from the grid is **enabled by default** for enterprise users. To copy your selection to the system clipboard, you can use the keybind `^ Ctrl`+`C`, or right click on a cell and select 'Copy' from the context menu. Unless [Cell Selection](https://www.ag-grid.com/react-data-grid/cell-selection/) or [Row Selection](https://www.ag-grid.com/react-data-grid/row-selection/) is enabled, you will only be copying from the currently focused cell.

When copying multiple cells, the contents will be copied in an Excel compatible format, with fields separated by a `\t` (tab) character.

## Copying Cell Ranges

When [Cell Ranges](https://www.ag-grid.com/react-data-grid/cell-selection/) are enabled by setting `gridOptions.cellSelection=true`, copying will copy the Cell Range's content to your clipboard. Select a range by clicking on a cell and dragging with the mouse, then copy with the `^ Ctrl`+`C` keybind.

Multiple cell ranges can be selected at once using `^ Ctrl` and dragging with the mouse. When copying, all ranges will be copied to the clipboard. Note that the relative positions of multiple ranges is not preserved when copying, they are stacked vertically in the clipboard.

In the below example try:

- Select a cell range with click & drag
- Copy with `^ Ctrl`+`C`
- Paste into an external program / text editor.

#### Copying Cell Ranges

```tsx
"use client";

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
  CellSelectionOptions,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  ModuleRegistry,
  NumberEditorModule,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  CellSelectionModule,
  ClipboardModule,
  ColumnMenuModule,
  ContextMenuModule,
} from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  NumberEditorModule,
  TextEditorModule,
  ClientSideRowModelModule,
  ClipboardModule,
  ColumnMenuModule,
  ContextMenuModule,
  CellSelectionModule,
];

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "athlete", minWidth: 200 },
    { field: "age" },
    { field: "country", minWidth: 150 },
    { field: "year" },
    { field: "date", minWidth: 150 },
    { field: "sport", minWidth: 150 },
    { field: "gold" },
    { field: "silver" },
    { field: "bronze" },
    { field: "total" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      editable: true,
      flex: 1,
      minWidth: 100,
    };
  }, []);

  const { data, loading } = useFetchJson<IOlympicData>(
    "https://www.ag-grid.com/example-assets/olympic-winners.json",
  );

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact<IOlympicData>
            rowData={data}
            loading={loading}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            cellSelection={true}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Copying Cell Ranges](https://www.ag-grid.com/examples/clipboard/copy-range/reactFunctionalTs)

## Copying Rows

When [Row Selection](https://www.ag-grid.com/react-data-grid/row-selection/) is enabled by setting `gridOptions.rowSelection.mode` to either `"singleRow"` or `"multiRow"`, the default behaviour is to copy only the focused cell to the clipboard. In order to copy the whole row, enable the `gridOptions.rowSelection.copySelectedRows` flag.

```jsx
const rowSelection = useMemo(() => { 
	return {
        mode: 'multiRow',
        copySelectedRows: true,
    };
}, []);

<AgGridReact rowSelection={rowSelection} />
```

The below example demonstrates copying rows. Initially, pressing `^ Ctrl`+C will select the focused cell, regardless of which rows have been selected. You can change this behaviour by toggling the "Copy Selected Rows" checkbox to enable the `copySelectedRows` flag:

- Toggle the "Copy Selected Rows" checkbox on
- Select one or more rows in the example below and press `^ Ctrl`+C
- Paste copied content in a text editor
- When the "Copy Selected Rows" checkbox is toggled on, the pasted content includes all selected rows. When the "Copy Selected Rows" checkbox is toggled off, the pasted content includes only the focussed cell.

#### Copying Rows

```tsx
"use client";

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import "./style.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  ModuleRegistry,
  NumberEditorModule,
  RowSelectionModule,
  RowSelectionOptions,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  CellSelectionModule,
  ClipboardModule,
  ColumnMenuModule,
  ContextMenuModule,
} from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  NumberEditorModule,
  TextEditorModule,
  ClientSideRowModelModule,
  ClipboardModule,
  ColumnMenuModule,
  ContextMenuModule,
  CellSelectionModule,
  RowSelectionModule,
];

const GridExample = () => {
  const gridRef = useRef<AgGridReact<IOlympicData>>(null);
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "athlete", minWidth: 200 },
    { field: "age" },
    { field: "country", minWidth: 150 },
    { field: "year" },
    { field: "date", minWidth: 150 },
    { field: "sport", minWidth: 150 },
    { field: "gold" },
    { field: "silver" },
    { field: "bronze" },
    { field: "total" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      editable: true,
      flex: 1,
      minWidth: 100,
    };
  }, []);
  const rowSelection = useMemo<
    RowSelectionOptions | "single" | "multiple"
  >(() => {
    return {
      mode: "multiRow",
      copySelectedRows: false,
    };
  }, []);

  const { data, loading } = useFetchJson<IOlympicData>(
    "https://www.ag-grid.com/example-assets/olympic-winners.json",
  );

  const toggleCopyRows = useCallback(() => {
    gridRef.current!.api.setGridOption("rowSelection", {
      mode: "multiRow",
      copySelectedRows:
        document.querySelector<HTMLInputElement>("#toggle-copy-rows")
          ?.checked ?? false,
    });
  }, []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div className="example-wrapper">
          <div className="example-header">
            <label htmlFor="toggle-copy-rows">Copy Selected Rows: </label>
            <input
              type="checkbox"
              id="toggle-copy-rows"
              onChange={toggleCopyRows}
            />
          </div>

          <div style={gridStyle}>
            <AgGridReact<IOlympicData>
              ref={gridRef}
              rowData={data}
              loading={loading}
              columnDefs={columnDefs}
              defaultColDef={defaultColDef}
              rowSelection={rowSelection}
            />
          </div>
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Copying Rows](https://www.ag-grid.com/examples/clipboard/copy-row/reactFunctionalTs)

## Copying Headers

The column headers can be copied to the clipboard in addition to the cell contents by enabling the option `copyHeadersToClipboard`.

```jsx
const copyHeadersToClipboard = true;

<AgGridReact copyHeadersToClipboard={copyHeadersToClipboard} />
```

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `copyHeadersToClipboard` | `boolean` |  | `false` | Set to `true` to also include headers when copying to clipboard using ^ Ctrl+C clipboard. Default: `false` Module: [`ClipboardModule`](https://www.ag-grid.com/react-data-grid/modules/). |

In the below example try:

- Select a cell range with click & drag
- Copy with `^ Ctrl`+`C`
- Paste into an external program / text editor, note that the column headers were also copied.

#### Copying Cell Ranges and Headers

```tsx
"use client";

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
  CellSelectionOptions,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  ModuleRegistry,
  NumberEditorModule,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  CellSelectionModule,
  ClipboardModule,
  ColumnMenuModule,
  ContextMenuModule,
} from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  NumberEditorModule,
  TextEditorModule,
  ClientSideRowModelModule,
  ClipboardModule,
  ColumnMenuModule,
  ContextMenuModule,
  CellSelectionModule,
];

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "athlete", minWidth: 200 },
    { field: "age" },
    { field: "country", minWidth: 150 },
    { field: "year" },
    { field: "date", minWidth: 150 },
    { field: "sport", minWidth: 150 },
    { field: "gold" },
    { field: "silver" },
    { field: "bronze" },
    { field: "total" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      editable: true,
      flex: 1,
      minWidth: 100,
    };
  }, []);

  const { data, loading } = useFetchJson<IOlympicData>(
    "https://www.ag-grid.com/example-assets/olympic-winners.json",
  );

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact<IOlympicData>
            rowData={data}
            loading={loading}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            cellSelection={true}
            copyHeadersToClipboard={true}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Copying Cell Ranges and Headers](https://www.ag-grid.com/examples/clipboard/copy-range-with-headers/reactFunctionalTs)

## Custom Clipboard Interaction

If you want to do the copy to clipboard yourself (i.e. not use the grid's clipboard interaction) then implement the callback `sendToClipboard(params)`. Use this if you are in a non-standard web container that has a bespoke API for interacting with the clipboard. The callback gets the data to go into the clipboard, it's your job to call the bespoke API.

The example below shows using `sendToClipboard(params)`, but rather than using the clipboard, demonstrates the callback by just printing the data to the console.

#### Controlling Clipboard Copy

```tsx
("use client");

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
  CellSelectionOptions,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  ModuleRegistry,
  NumberEditorModule,
  RowSelectionModule,
  RowSelectionOptions,
  SendToClipboard,
  SendToClipboardParams,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  CellSelectionModule,
  ClipboardModule,
  ColumnMenuModule,
  ContextMenuModule,
} from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  NumberEditorModule,
  TextEditorModule,
  RowSelectionModule,
  ClientSideRowModelModule,
  ClipboardModule,
  ColumnMenuModule,
  ContextMenuModule,
  CellSelectionModule,
];

const GridExample = () => {
  const gridRef = useRef<AgGridReact<IOlympicData>>(null);
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "athlete", minWidth: 200 },
    { field: "age" },
    { field: "country", minWidth: 150 },
    { field: "year" },
    { field: "date", minWidth: 150 },
    { field: "sport", minWidth: 150 },
    { field: "gold" },
    { field: "silver" },
    { field: "bronze" },
    { field: "total" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      editable: true,
      flex: 1,
      minWidth: 100,
    };
  }, []);
  const rowSelection = useMemo<
    RowSelectionOptions | "single" | "multiple"
  >(() => {
    return { mode: "multiRow" };
  }, []);

  const { data, loading } = useFetchJson<IOlympicData>(
    "https://www.ag-grid.com/example-assets/olympic-winners.json",
  );

  const onBtCopyRows = useCallback(() => {
    gridRef.current!.api.copySelectedRowsToClipboard();
  }, []);

  const onBtCopyRange = useCallback(() => {
    gridRef.current!.api.copySelectedRangeToClipboard();
  }, []);

  const sendToClipboard = useCallback((params: SendToClipboardParams) => {
    console.log("send to clipboard called with data:");
    console.log(params.data);
  }, []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div
          style={{ display: "flex", height: "100%", flexDirection: "column" }}
        >
          <div style={{ paddingBottom: "5px" }}>
            <button onClick={onBtCopyRows}>
              Copy Selected Rows to Clipboard
            </button>
            <button onClick={onBtCopyRange}>
              Copy Selected Range to Clipboard
            </button>
          </div>

          <div style={gridStyle}>
            <AgGridReact<IOlympicData>
              ref={gridRef}
              rowData={data}
              loading={loading}
              columnDefs={columnDefs}
              defaultColDef={defaultColDef}
              cellSelection={true}
              rowSelection={rowSelection}
              sendToClipboard={sendToClipboard}
            />
          </div>
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Controlling Clipboard Copy](https://www.ag-grid.com/examples/clipboard/custom/reactFunctionalTs)

## Copying via the API

You can use the Grid API methods: `copySelectedRowsToClipboard(...)` and `copySelectedRangeToClipboard(...)` to copy rows or ranges respectively, these API calls take optional parameters to enable copying column and group headers.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `copySelectedRangeToClipboard` | `Function` |  |  | Copies the selected ranges to the clipboard. Module: [`ClipboardModule`](https://www.ag-grid.com/react-data-grid/modules/). |
| `copySelectedRowsToClipboard` | `Function` |  |  | Copies the selected rows to the clipboard. Module: [`ClipboardModule`](https://www.ag-grid.com/react-data-grid/modules/). |

## How to Cut

Cut from the grid is **enabled by default** for enterprise users. To cut your selection to the system clipboard, you can use the keybind `^ Ctrl`+`X`, or right click on a cell and select 'Cut' from the context menu. Unless [Range Selection](https://www.ag-grid.com/react-data-grid/cell-selection/) or [Row Selection](https://www.ag-grid.com/react-data-grid/row-selection/) is enabled, you will only be copying from the currently focused cell.

The cut operations will work exactly the same as the copy operations, with the addition that data will be removed from the grid afterwards, so the cut operations will use the same properties described above to customise the `copy` process.

## Disabling Cut

Since `Cut` is a destructive process, the `suppressCutToClipboard` property was added to the Grid Options.

```jsx
const suppressCutToClipboard = true;

<AgGridReact suppressCutToClipboard={suppressCutToClipboard} />
```

This is demonstrated in the example below. Note the following:

- Selecting a cell and pressing `^ Ctrl`+`X` will not `copy` or `cut` the data.
- The context menu will not show an option to `Cut`.

#### Clipboard Suppress Cut

```tsx
"use client";

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
  CellSelectionOptions,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  ModuleRegistry,
  NumberEditorModule,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  CellSelectionModule,
  ClipboardModule,
  ColumnMenuModule,
  ContextMenuModule,
} from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  NumberEditorModule,
  TextEditorModule,
  ClientSideRowModelModule,
  ClipboardModule,
  ColumnMenuModule,
  ContextMenuModule,
  CellSelectionModule,
];

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "athlete", minWidth: 200 },
    { field: "age" },
    { field: "country", minWidth: 150 },
    { field: "year" },
    { field: "date", minWidth: 150 },
    { field: "sport", minWidth: 150 },
    { field: "gold" },
    { field: "silver" },
    { field: "bronze" },
    { field: "total" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      editable: true,
      flex: 1,
      minWidth: 100,
    };
  }, []);

  const { data, loading } = useFetchJson<IOlympicData>(
    "https://www.ag-grid.com/example-assets/olympic-winners.json",
  );

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact<IOlympicData>
            rowData={data}
            loading={loading}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            cellSelection={true}
            suppressCutToClipboard={true}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Clipboard Suppress Cut](https://www.ag-grid.com/examples/clipboard/suppress-cut/reactFunctionalTs)

## How to Paste

Paste is enabled by default in the enterprise version and is possible as long as the cells you're pasting into are [editable](https://www.ag-grid.com/react-data-grid/cell-editing/) (non-editable cells cannot be modified, even with a paste operation). You can paste using the keybind `^ Ctrl`+`V` while focus is on the grid.

The behaviour of paste changes depending on whether you have a single cell or a range selected:

- When a **single cell is selected**. The paste will proceed starting at the selected cell if multiple cells are to be pasted.
- When a **range of cells selected**. If the selected range being pasted is larger than copied range, it will repeat if it fits evenly, otherwise it will just copy the cells into the start of the range.

## Disabling Paste

You can turn paste operations off for the entire grid, by setting the grid property `suppressClipboardPaste=true`.

Or you can disable pasting for a specific column or cell by setting the property `suppressPaste` on the column definition. This can be a boolean or a function (use a function to specify for a particular cell, or boolean for the whole column).

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `suppressPaste` | `boolean \| SuppressPasteCallback` |  |  | Pasting is on by default as long as cells are editable (non-editable cells cannot be modified, even with a paste operation). Set to `true` turn paste operations off. |

## Processing Pasted Data

The clipboard data will be processed by default [Using the Value Formatter for Export](https://www.ag-grid.com/react-data-grid/value-formatters/#formatting-for-export) to format the cells when copied, and [Using the Value Parser for Import](https://www.ag-grid.com/react-data-grid/value-parsers/#use-value-parser-for-import) to format the cells when pasted.

It is possible to override this behaviour specifically for the clipboard. This can be done either on individual cells or the whole paste operation.

### Processing Individual Cells

The interfaces and parameters for processing individual cells are as follows:

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `processCellForClipboard` | `ProcessCellForClipboard` |  |  | Allows you to process cells for the clipboard. Handy if for example you have `Date` objects that need to have a particular format if importing into Excel. Module: [`ClipboardModule`](https://www.ag-grid.com/react-data-grid/modules/). |
| `processHeaderForClipboard` | `ProcessHeaderForClipboard` |  |  | Allows you to process header values for the clipboard. Module: [`ClipboardModule`](https://www.ag-grid.com/react-data-grid/modules/). |
| `processGroupHeaderForClipboard` | `ProcessGroupHeaderForClipboard` |  |  | Allows you to process group header values for the clipboard. Module: [`ClipboardModule`](https://www.ag-grid.com/react-data-grid/modules/). |
| `processCellFromClipboard` | `ProcessCellFromClipboard` |  |  | Allows you to process cells from the clipboard. Handy if for example you have number fields and want to block non-numbers from getting into the grid. Module: [`ClipboardModule`](https://www.ag-grid.com/react-data-grid/modules/). |

These three callbacks above are demonstrated in the example below. Note the following:

- When cells are copied to the clipboard, values are prefixed with 'C-'. Cells can be copied by dragging a range with the mouse and hitting `^ Ctrl`+`C`.
- When cells are pasted from the clipboard, values are prefixed with 'Z-'. Cells can be pasted by hitting `^ Ctrl`+`V`.
- When headers are copied to the clipboard, values are prefixed with 'H-'. Headers can be copied by using the context menu.
- When group headers are copied to the clipboard, values are prefixed with 'GH-'. Headers can be copied by using the context menu.

#### Example Process

```tsx
"use client";

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
  CellSelectionOptions,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  ModuleRegistry,
  NumberEditorModule,
  ProcessCellForClipboard,
  ProcessCellForExportParams,
  ProcessCellFromClipboard,
  ProcessGroupHeaderForClipboard,
  ProcessGroupHeaderForExportParams,
  ProcessHeaderForClipboard,
  ProcessHeaderForExportParams,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  CellSelectionModule,
  ClipboardModule,
  ColumnMenuModule,
  ContextMenuModule,
} from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  NumberEditorModule,
  TextEditorModule,
  ClientSideRowModelModule,
  ClipboardModule,
  ColumnMenuModule,
  ContextMenuModule,
  CellSelectionModule,
];

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);

  const [columnDefs, setColumnDefs] = useState<(ColDef | ColGroupDef)[]>([
    {
      headerName: "Participants",
      children: [
        { field: "athlete", headerName: "Athlete Name", minWidth: 200 },
        { field: "age" },
        { field: "country", minWidth: 150 },
      ],
    },
    {
      headerName: "Olympic Games",
      children: [
        { field: "year" },
        { field: "date", minWidth: 150 },
        { field: "sport", minWidth: 150 },
        { field: "gold" },
        { field: "silver", suppressPaste: true },
        { field: "bronze" },
        { field: "total" },
      ],
    },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      editable: true,
      flex: 1,
      minWidth: 100,
      cellDataType: false,
    };
  }, []);

  const { data, loading } = useFetchJson<IOlympicData>(
    "https://www.ag-grid.com/example-assets/olympic-winners.json",
  );

  const processCellForClipboard = useCallback(
    (params: ProcessCellForExportParams) => {
      return "C-" + params.value;
    },
    [],
  );

  const processHeaderForClipboard = useCallback(
    (params: ProcessHeaderForExportParams) => {
      const colDef = params.column.getColDef();
      let headerName = colDef.headerName || colDef.field || "";
      if (colDef.headerName !== "") {
        headerName = headerName.charAt(0).toUpperCase() + headerName.slice(1);
      }
      return "H-" + headerName;
    },
    [],
  );

  const processGroupHeaderForClipboard = useCallback(
    (params: ProcessGroupHeaderForExportParams) => {
      const colGroupDef = params.columnGroup.getColGroupDef() || ({} as any);
      const headerName = colGroupDef.headerName || "";
      if (headerName === "") {
        return "";
      }
      return "GH-" + headerName;
    },
    [],
  );

  const processCellFromClipboard = useCallback(
    (params: ProcessCellForExportParams) => {
      return "Z-" + params.value;
    },
    [],
  );

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact<IOlympicData>
            rowData={data}
            loading={loading}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            cellSelection={true}
            processCellForClipboard={processCellForClipboard}
            processHeaderForClipboard={processHeaderForClipboard}
            processGroupHeaderForClipboard={processGroupHeaderForClipboard}
            processCellFromClipboard={processCellFromClipboard}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Example Process](https://www.ag-grid.com/examples/clipboard/process/reactFunctionalTs)

### Processing Data from Clipboard

To have complete control of processing clipboard data when pasting, you can use the callback below:

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `processDataFromClipboard` | `ProcessDataFromClipboard` |  |  | Allows complete control of the paste operation, including cancelling the operation (so nothing happens) or replacing the data with other data. Module: [`ClipboardModule`](https://www.ag-grid.com/react-data-grid/modules/). |

The following example shows custom code to process the data from the clipboard:

- The cells are coloured based on the colour that the cell content starts with
- Copy a cell range in the grid which includes a cell value that starts with `Red`. Pasting into the grid will paste a custom 4x4 cell grid.
- Copy a cell range in the grid which includes a cell value that starts with `Yellow` and **doesn’t** include any `Red` cell values. Pasting this copied cell range will cancel the paste action and not paste anything
- Any other copied cell data will be pasted as-is

#### Example Process Data

```tsx
"use client";

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import "./style.css";
import {
  CellSelectionOptions,
  CellStyleModule,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  ModuleRegistry,
  ProcessDataFromClipboard,
  ProcessDataFromClipboardParams,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  CellSelectionModule,
  ClipboardModule,
  ColumnMenuModule,
  ContextMenuModule,
} from "ag-grid-enterprise";
import { getData } from "./data";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  TextEditorModule,
  CellStyleModule,
  ClientSideRowModelModule,
  ClipboardModule,
  ColumnMenuModule,
  ContextMenuModule,
  CellSelectionModule,
];

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
  const [rowData, setRowData] = useState<any[]>(getData());
  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "a" },
    { field: "b" },
    { field: "c" },
    { field: "d" },
    { field: "e" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      editable: true,
      minWidth: 120,
      flex: 1,
      cellClassRules: {
        "cell-green": 'value && value.startsWith("Green")',
        "cell-blue": 'value && value.startsWith("Blue")',
        "cell-red": 'value && value.startsWith("Red")',
        "cell-yellow": 'value && value.startsWith("Yellow")',
      },
    };
  }, []);

  const processDataFromClipboard = useCallback(
    (params: ProcessDataFromClipboardParams): string[][] | null => {
      let containsRed;
      let containsYellow;
      const data = params.data;
      for (let i = 0; i < data.length; i++) {
        const row = data[i];
        for (let j = 0; j < row.length; j++) {
          const value = row[j];
          if (value) {
            if (value.startsWith("Red")) {
              containsRed = true;
            } else if (value.startsWith("Yellow")) {
              containsYellow = true;
            }
          }
        }
      }
      if (containsRed) {
        // replace the paste request with another
        return [
          ["Custom 1", "Custom 2"],
          ["Custom 3", "Custom 4"],
        ];
      }
      if (containsYellow) {
        // cancels the paste
        return null;
      }
      return data;
    },
    [],
  );

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact
            rowData={rowData}
            columnDefs={columnDefs}
            cellSelection={true}
            defaultColDef={defaultColDef}
            processDataFromClipboard={processDataFromClipboard}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Example Process Data](https://www.ag-grid.com/examples/clipboard/process-all/reactFunctionalTs)

### Pasting New Rows at the Bottom of the Grid

By default, when pasting multiple rows near the last record shown in the grid, any rows exceeding the total number of rows shown in the grid will not be pasted.

In order to insert all the copied rows in the grid, a custom `processDataFromClipboard` function is needed to add the necessary number of new rows using the [Transaction Update API](https://www.ag-grid.com/react-data-grid/data-update-transactions/#transaction-update-api).

The example below uses a custom `processDataFromClipboard` function to add new rows to the grid, to fit all the copied rows:

- Select the top 3 rows in the grid using `⇧ Shift` + click
- Press `^ Ctrl`+`C` to copy the selected rows
- Select the `Ryan Lochte` cell on the last row and press `^ Ctrl`+`V` to paste the copied rows
- Notice that the `Ryan Lochte` row has been overwritten and 2 extra rows are created at the bottom of the grid to accommodate the additional 2 rows pasted

#### Paste New Rows

```tsx
"use client";

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
  ClientSideRowModelApiModule,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  ColumnApiModule,
  GridApi,
  GridOptions,
  ModuleRegistry,
  NumberEditorModule,
  ProcessDataFromClipboard,
  ProcessDataFromClipboardParams,
  RowApiModule,
  RowSelectionModule,
  RowSelectionOptions,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  CellSelectionModule,
  ClipboardModule,
  ColumnMenuModule,
  ContextMenuModule,
} from "ag-grid-enterprise";
import { useFetchJson } from "./useFetchJson";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  ColumnApiModule,
  RowApiModule,
  ClientSideRowModelApiModule,
  NumberEditorModule,
  TextEditorModule,
  RowSelectionModule,
  ClientSideRowModelModule,
  ClipboardModule,
  ColumnMenuModule,
  ContextMenuModule,
  CellSelectionModule,
];

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { headerName: "Athlete", field: "athlete", width: 150 },
    { headerName: "Age", field: "age", width: 90 },
    { headerName: "Country", field: "country", width: 120 },
    { headerName: "Year", field: "year", width: 90 },
    { headerName: "Date", field: "date", width: 110 },
    { headerName: "Sport", field: "sport", width: 110 },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      editable: true,
    };
  }, []);
  const rowSelection = useMemo<
    RowSelectionOptions | "single" | "multiple"
  >(() => {
    return {
      mode: "multiRow",
      checkboxes: false,
      headerCheckbox: false,
      enableClickSelection: true,
      copySelectedRows: true,
    };
  }, []);

  const { data, loading } = useFetchJson<any>(
    "https://www.ag-grid.com/example-assets/olympic-winners.json",
    8,
  );

  const processDataFromClipboard = useCallback(
    (params: ProcessDataFromClipboardParams): string[][] | null => {
      const data = [...params.data];
      const emptyLastRow =
        data[data.length - 1][0] === "" && data[data.length - 1].length === 1;
      if (emptyLastRow) {
        data.splice(data.length - 1, 1);
      }
      const lastIndex = params.api!.getDisplayedRowCount() - 1;
      const focusedCell = params.api!.getFocusedCell();
      const focusedIndex = focusedCell!.rowIndex;
      if (focusedIndex + data.length - 1 > lastIndex) {
        const resultLastIndex = focusedIndex + (data.length - 1);
        const numRowsToAdd = resultLastIndex - lastIndex;
        const rowsToAdd: any[] = [];
        for (let i = 0; i < numRowsToAdd; i++) {
          const index = data.length - 1;
          const row = data.slice(index, index + 1)[0];
          // Create row object
          const rowObject: any = {};
          let currentColumn: any = focusedCell!.column;
          row.forEach((item) => {
            if (!currentColumn) {
              return;
            }
            rowObject[currentColumn.colDef.field] = item;
            currentColumn = params.api!.getDisplayedColAfter(currentColumn);
          });
          rowsToAdd.push(rowObject);
        }
        params.api!.applyTransaction({ add: rowsToAdd });
      }
      return data;
    },
    [],
  );

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact
            rowData={data}
            loading={loading}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            rowSelection={rowSelection}
            processDataFromClipboard={processDataFromClipboard}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Paste New Rows](https://www.ag-grid.com/examples/clipboard/pasting-extra-rows/reactFunctionalTs)

### Read Only Edit

When the grid is in [Read Only Edit](https://www.ag-grid.com/react-data-grid/value-setters/#read-only-edit) mode the `Clipboard` will not update the data inside the grid. Instead the grid fires `cellEditRequest` events allowing the application to process the update request.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `cellEditRequest` | `CellEditRequestEvent` |  |  | Value has changed after editing. Only fires when `readOnlyEdit=true`. |

The example below will show how to update cell value combining the `Clipboard` with `readOnlyEdit=true`.

#### Clipboard - ReadOnlyEdit

```tsx
("use client");

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
  CellEditRequestEvent,
  CellSelectionOptions,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GetRowIdFunc,
  GetRowIdParams,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  NumberEditorModule,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import { CellSelectionModule, ClipboardModule } from "ag-grid-enterprise";
import { IOlympicDataWithId } from "./interfaces";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  NumberEditorModule,
  TextEditorModule,
  ClientSideRowModelModule,
  ClipboardModule,
  CellSelectionModule,
];

let rowImmutableStore: any[];

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
  const [rowData, setRowData] = useState<IOlympicDataWithId[]>();
  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "athlete", minWidth: 160 },
    { field: "age" },
    { field: "country", minWidth: 140 },
    { field: "year" },
    { field: "date", minWidth: 140 },
    { field: "sport", minWidth: 160 },
    { field: "gold" },
    { field: "silver" },
    { field: "bronze" },
    { field: "total" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 100,
      editable: true,
    };
  }, []);
  const getRowId = useCallback(
    (params: GetRowIdParams) => String(params.data.id),
    [],
  );

  const onGridReady = useCallback((params: GridReadyEvent) => {
    fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .then((resp) => resp.json())
      .then((data: IOlympicDataWithId[]) => {
        data.forEach((item, index) => (item.id = index));
        rowImmutableStore = data;
        setRowData(rowImmutableStore);
      });
  }, []);

  const onCellEditRequest = useCallback(
    (event: CellEditRequestEvent) => {
      const data = event.data;
      const field = event.colDef.field;
      const newValue = event.newValue;
      const oldItem = rowImmutableStore.find((row) => row.id === data.id);
      if (!oldItem || !field) {
        return;
      }
      const newItem = { ...oldItem };
      newItem[field] = newValue;
      console.log("onCellEditRequest, updating " + field + " to " + newValue);
      rowImmutableStore = rowImmutableStore.map((oldItem) =>
        oldItem.id == newItem.id ? newItem : oldItem,
      );
      setRowData(rowImmutableStore);
    },
    [rowImmutableStore],
  );

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact<IOlympicDataWithId>
            rowData={rowData}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            getRowId={getRowId}
            cellSelection={true}
            readOnlyEdit={true}
            onGridReady={onGridReady}
            onCellEditRequest={onCellEditRequest}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Clipboard - ReadOnlyEdit](https://www.ag-grid.com/examples/clipboard/read-only-edit/reactFunctionalTs)

## Changing the Delimiter for Copy & Paste

By default, the grid will use `\t` (tab) as the field delimiter. This is to keep the copy / paste compatible with Excel. If you want another delimiter then you can set the property `gridOptions.clipboardDelimiter` to a value of your choosing.

## Using the Browser's Text Selection

The grid's selection and copy features replace the built-in browser behaviour for selecting and copying text. If you want to use the normal browser behaviour instead, you should set `enableCellTextSelection=true` in the gridOptions. Note the following:

- When `enableCellTextSelection=true`, pressing `^ Ctrl`+`C` doesn’t copy the focused cell value, but only the selected text inside the grid cell. When using AG Grid Enterprise, the user can copy the entire cell value by right-clicking the grid cell to show the [context menu](https://www.ag-grid.com/react-data-grid/context-menu/) and clicking the any of the Copy menu items.
- When `enableCellTextSelection=true`, the option `ensureDomOrder=true` needs to be set for correct accessibility support. See [Ensure DOM Element order](https://www.ag-grid.com/react-data-grid/accessibility/#ensure-dom-element-order).

> **Note**
>
> This is not an enterprise config and can be used at any time to enable cell text selection.

See this behaviour shown in the example below:

- Focus a grid cell and press `^ Ctrl`+`C`. The cell value will not be copied because no text is selected.
- Click a cell and drag across its value to select the text. Press `^ Ctrl`+`C` and the value will be copied.
- This sample is using AG Grid Community version, so the context menu is not available to copy the focused cell value. In AG Grid Enterprise the context menu will be available to copy the entire focused cell value without having to select it as text.

#### Using Browser text selection

```tsx
"use client";

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  ModuleRegistry,
  NumberEditorModule,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  NumberEditorModule,
  TextEditorModule,
  ClientSideRowModelModule,
];

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "athlete", minWidth: 200 },
    { field: "age" },
    { field: "country", minWidth: 150 },
    { field: "year" },
    { field: "date", minWidth: 150 },
    { field: "sport", minWidth: 150 },
    { field: "gold" },
    { field: "silver", suppressPaste: true },
    { field: "bronze" },
    { field: "total" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      editable: true,
      flex: 1,
      minWidth: 100,
    };
  }, []);

  const { data, loading } = useFetchJson<IOlympicData>(
    "https://www.ag-grid.com/example-assets/olympic-winners.json",
  );

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact<IOlympicData>
            rowData={data}
            loading={loading}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            ensureDomOrder={true}
            enableCellTextSelection={true}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Using Browser text selection](https://www.ag-grid.com/examples/clipboard/cellTextSelection/reactFunctionalTs)

## Clipboard Events

The following events are relevant to clipboard operations:

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `cutStart` | `CutStartEvent` |  |  | Cut operation has started. |
| `cutEnd` | `CutEndEvent` |  |  | Cut operation has ended. |
| `pasteStart` | `PasteStartEvent` |  |  | Paste operation has started. |
| `pasteEnd` | `PasteEndEvent` |  |  | Paste operation has ended. |

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `cellValueChanged` | `CellValueChangedEvent` |  |  | Cell value has changed. This occurs after the following scenarios: - Editing. Will not fire if any of the following are true: new value is the same as old value; `readOnlyEdit = true`; editing was cancelled (e.g. Escape key was pressed); or new value is of the wrong cell data type for the column. - Cut. - Paste. - Cell clear (pressing Delete key). - Fill handle. - Copy range down. - Undo and redo. See [Editing Events](https://www.ag-grid.com/react-data-grid/cell-editing/#editing-events) for more information. |

For a cut or paste operation the events will be fired as:

1. One `cutStart`/`pasteStart` event.
2. Many `cellValueChanged` events.
3. One `cutEnd`/`pasteEnd` event.

If the application is doing work each time it receives a `cellValueChanged`, you can use the `cutStart`/`pasteStart` and `cutEnd`/`pasteEnd` events to suspend the applications work and then do the work for all cells impacted by the cut/paste operation after the cut/paste operation.

There are no events triggered by copy to clipboard as this does not change the grid's data.

#### Clipboard Events

```tsx
("use client");

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
  CellSelectionOptions,
  CellValueChangedEvent,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  CutEndEvent,
  CutStartEvent,
  GridApi,
  GridOptions,
  ModuleRegistry,
  NumberEditorModule,
  PasteEndEvent,
  PasteStartEvent,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  CellSelectionModule,
  ClipboardModule,
  ColumnMenuModule,
  ContextMenuModule,
} from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  NumberEditorModule,
  TextEditorModule,
  ClientSideRowModelModule,
  ClipboardModule,
  ColumnMenuModule,
  ContextMenuModule,
  CellSelectionModule,
];

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "athlete", minWidth: 200 },
    { field: "age" },
    { field: "country", minWidth: 150 },
    { field: "year" },
    { field: "date", minWidth: 150 },
    { field: "sport", minWidth: 150 },
    { field: "gold" },
    { field: "silver" },
    { field: "bronze" },
    { field: "total" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      editable: true,
      flex: 1,
      minWidth: 100,
    };
  }, []);

  const { data, loading } = useFetchJson<IOlympicData>(
    "https://www.ag-grid.com/example-assets/olympic-winners.json",
  );

  const onCellValueChanged = useCallback((params: CellValueChangedEvent) => {
    console.log("Callback onCellValueChanged:", params);
  }, []);

  const onCutStart = useCallback((params: CutStartEvent) => {
    console.log("Callback onCutStart:", params);
  }, []);

  const onCutEnd = useCallback((params: CutEndEvent) => {
    console.log("Callback onCutEnd:", params);
  }, []);

  const onPasteStart = useCallback((params: PasteStartEvent) => {
    console.log("Callback onPasteStart:", params);
  }, []);

  const onPasteEnd = useCallback((params: PasteEndEvent) => {
    console.log("Callback onPasteEnd:", params);
  }, []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact<IOlympicData>
            rowData={data}
            loading={loading}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            cellSelection={true}
            onCellValueChanged={onCellValueChanged}
            onCutStart={onCutStart}
            onCutEnd={onCutEnd}
            onPasteStart={onPasteStart}
            onPasteEnd={onPasteEnd}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Clipboard Events](https://www.ag-grid.com/examples/clipboard/clipboard-events/reactFunctionalTs)
