Search docs

Search blocks and features…

Copy/Paste & Undo

Excel/Sheets-compatible TSV copy/paste, a single undo/redo history across edits, and bulk-clear.

Installation

Copy/paste and undo ship with Data Table — no separate install.

Works with:Data Table
npx shadcn@latest add @kotsas-ui/data-table

This feature confirms its results with toasts. The install adds components/ui/sonner.tsx for you, but the <Toaster /> it exports must be mounted once in your root layout — otherwise the table works but its feedback never appears.

app/layout.tsx
import { Toaster } from "@/components/ui/sonner"

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Toaster />
      </body>
    </html>
  )
}

Usage

Try it in the demo: edit a cell, copy a selection (Ctrl/Cmd+C), paste a block (Ctrl/Cmd+V), then undo (Ctrl/Cmd+Z) — a multi-cell paste or a selection-wide clear undoes as a single step, not one step per cell.

Set up CI pipeline
High
Done
$2,400.00
True
Design onboarding flow
Medium
In progress
$3,200.00
False
Migrate auth to OAuth2
Urgent
In progress
$5,600.00
False
Write API docs
Low
To do
$900.00
False

Keyboard shortcuts

KeysAction
Ctrl/Cmd+ZUndo the last edit, paste, or bulk-clear.
Ctrl/Cmd+Shift+ZRedo.
Ctrl/Cmd+CCopy the active cell, or every visible column of every selected row, as TSV.
Ctrl/Cmd+VPaste a TSV block starting at the active cell; rows past the last one are reported via onCreateRows.
Delete / BackspaceClear every editable column of the selected rows, or just the active cell if nothing is selected.

Examples

Copy/paste serialization is per-column, populated automatically by defineColumns from each field's own toClipboard/fromClipboard. A raw ColumnDef can opt in the same way via meta:

col.currency("budget", { currency: "USD" })
// meta.toClipboard / meta.fromClipboard are populated automatically by
// defineColumns from the field's own serializers. A raw ColumnDef (the
// escape hatch, not built via col.*) can opt in the same way:
{
  id: "custom",
  meta: {
    label: "Custom",
    toClipboard: (v) => String(v ?? ""),
    fromClipboard: (text) => text, // return undefined to reject a pasted value
  },
}

API Reference

PropTypeDefaultDescription
editable?booleanfalseTable-level default for whether cells can be edited; per-column editable in defineColumns overrides it.
onUpdateData?(rowId, columnId, value) => voidCalled for every committed edit (typing, paste, bulk-clear, undo, redo). The library never mutates data itself — you own applying the change.
onCreateRows?(partialRows: Partial<TData>[]) => voidCalled when a paste extends past the last row, once per overflow row, with only the pasted columns populated.
meta.toClipboard?(value) => stringPer-column serializer for copy/export — populated automatically by defineColumns from the field type.
meta.fromClipboard?(text) => unknownPer-column parser for paste. Return undefined to reject a value; omit entirely to make a column paste-proof.

See also

Downloading the current view as a file is a separate feature — see Export Data.