Export
What you export is what you are looking at: the filtered, sorted rows, the visible columns in their order, the values formatted as the cells show them. The Export menu in the toolbar does it in one click; the api does the same from your own button, with raw values if you prefer.
| Aarav Smith | aarav.smith@corp.example | Paused | $2,133.00 | 4.6% | Jul 5, 2024 | nps-9eu | — | |
| Leila Reddy | leila.reddy@example.com | Paused | $48.00 | 99.6% | Jan 21, 2023 | nps-9 | — | |
| Kavya Dubois | kavya.dubois@example.com | Active | $0.00 | 15.9% | Dec 28, 2024 | legacy | — | |
| Chloe Garcia | chloe.garcia@example.com | Active | $4,582.00 | 70.3% | Feb 16, 2023 | — | — | |
| Aarav Müller | aarav.muller@example.com | Churned | $0.00 | 74.3% | Sep 7, 2026 | nps-9 | Needs onboarding call. | |
| Saanvi Das | saanvi.das@corp.example | Active | $1,711.00 | 13.9% | Mar 31, 2025 | apacbeta | — | |
| Freya Silva | freya.silva@corp.example | Churned | $0.00 | 62.5% | Oct 6, 2026 | — | — | |
| Saanvi Dubois | saanvi.dubois@example.com | Churned | $0.00 | 5.7% | Mar 10, 2024 | legacyvip | Wants SSO. | |
| Idris Khan | idris.khan@mail.test | Active | $0.00 | 34.7% | Mar 27, 2024 | legacy | — | |
| Mia Reddy | mia.reddy@example.com | Active | $0.00 | 45.9% | May 29, 2026 | — | — |
import { DownloadIcon } from "lucide-react";
import { Button } from "@/components/ui/button";import { DataGrid, createDataGridColumns, useDataGrid } from "@/components/ui/data-grid";
import { makePeople, STATUS_OPTIONS, type Person } from "@/lib/data-grid-demo-data";
const col = createDataGridColumns<Person>();
const columns = col.columns([ col.select(), col.text("name", { header: "Name" }), col.link("email", { header: "Email", size: 230, href: (v) => `mailto:${v}` }), col.enum("status", { header: "Status", options: STATUS_OPTIONS }), col.currency("mrr", { header: "MRR", currency: "USD" }), col.percent("usage", { header: "Usage" }), // A function supplies the exported value: ISO dates instead of the formatted display. col.date("joinedAt", { header: "Joined", export: (v) => v.toISOString().slice(0, 10) }), col.tags("tags", { header: "Tags" }), // Kept on screen, left out of every export. col.text("notes", { header: "Notes", export: false }),]);
const data = makePeople(80, 35);
/** The Export menu plus a custom button that exports raw values through the api. */export default function DataGridExport() { const grid = useDataGrid<Person>({ key: "export", data, columns, getRowId: (r) => r.id }); const selected = Object.keys(grid.table.state.rowSelection).length; const rows = grid.exportTable({ selectedOnly: selected > 0 }).rows.length; return ( <DataGrid grid={grid} toolbarActions={ <Button variant="outline" size="sm" onClick={() => grid.exportCsv("customers", { selectedOnly: selected > 0, formatted: false }) } > <DownloadIcon /> Raw CSV · {rows} {rows === 1 ? "row" : "rows"} </Button> } /> );}Use it
Section titled “Use it”The toolbar has the Export menu by default (the download icon):
| Item | Does |
|---|---|
| Download CSV | grid.exportCsv(filename) |
| Download JSON | grid.exportJson(filename) |
| Copy to clipboard | grid.copyToClipboard(): tab-separated, with a header row; announces “Copied n cells” with the row count |
| Selected rows only | A toggle, shown when row selection is on, disabled while nothing is selected. It applies to the three items above. |
Name the files with exportFilename on the toolbar (toolbar={{ exportFilename: "customers" }}),
or filename on <DataGridExportMenu> in a composed layout. Hide
the menu with toolbar={{ export: false }}.
From your own code:
const grid = useDataGrid({ data, columns })
grid.exportCsv("customers") // customers.csvgrid.exportJson("customers", { selectedOnly: true }) // the selected rowsgrid.exportCsv("customers-raw", { formatted: false }) // raw values, not display stringsawait grid.copyToClipboard({ visibleOnly: false }) // every exportable column, hidden tooconst table = grid.exportTable() // { headers, rows } for your own writerWhat goes in
Section titled “What goes in”Rows. Every row that passes the filters and the search, in the current sort order, across all
pages; paging never trims an export. With selectedOnly: true, only the selected rows that pass
the filters. With server-side data the grid only has the current
page, so that is what you get.
Columns. The visible leaf columns in display order (visibleOnly: false includes hidden
ones), minus the checkbox, expander, row-number and actions columns, minus columns with
export: false, and only columns that have an accessor or an export function (a display column
with neither has nothing to export). The header is the column’s label: label, else the string
header, else the id humanised.
Values. By default the same string the cell shows, from the column’s kind and formatting
options and the grid’s locale (currency symbols, date styles, enum labels, tags joined with
commas); blanks are "". With formatted: false, the raw value from the row: numbers stay
numbers in JSON, Dates become ISO strings in CSV, arrays and objects are JSON-encoded in CSV. A
column’s export function wins over both and receives (value, row).
col.date("joinedAt", { header: "Joined", export: (d) => d.toISOString().slice(0, 10) })col.text("notes", { header: "Notes", export: false })Formats
Section titled “Formats”| Format | Details |
|---|---|
| CSV | RFC 4180: cells holding a comma, a quote or a line break are quoted, quotes doubled. \r\n line ends. The file starts with a UTF-8 BOM so Excel opens accents correctly. MIME text/csv;charset=utf-8. |
| JSON | An array of objects keyed by header label, pretty-printed with two spaces. Missing cells are null. |
| TSV (clipboard) | Tabs between cells, \n line ends, header row included. Spreadsheets paste it as a block. |
The file name is the stem you pass (or the grid name), slugified: "Orders · 2026" becomes
orders-2026.csv; an empty stem falls back to export. Downloads are a Blob and a temporary
<a download>; outside a browser exportCsv and exportJson do nothing.
Options
Section titled “Options”Per call, DataGridExportOptions:
| Option | Type | Default | What it does |
|---|---|---|---|
selectedOnly |
boolean |
false |
Only the selected rows (that pass the filters). |
formatted |
boolean |
true |
Display strings (true) or raw values (false). |
visibleOnly |
boolean |
true |
Visible columns only, or every exportable column. |
Column options:
| Option | Type | Default | What it does |
|---|---|---|---|
export |
boolean | (value, row) => unknown |
true |
false leaves the column out; a function supplies the exported value. |
Toolbar props: export (show the menu, default true) and exportFilename.
| Member | What it does |
|---|---|
grid.exportTable(options?) |
{ headers: string[], rows: unknown[][] }, the model behind every format. |
grid.exportCsv(filename?, options?) |
Downloads <stem>.csv. |
grid.exportJson(filename?, options?) |
Downloads <stem>.json. |
grid.copyToClipboard(options?) |
Copies TSV with headers; resolves true on success. |
The pure helpers are exported too, for a custom format or a server route: toCsv(table),
toTsv(table, { headers }), toJson(table, { pretty }), toDelimited(table, delimiter, { headers, eol }),
csvCell(value, delimiter), downloadFile(filename, content, mime), copyText(text) and
slugifyFilename(name, fallback). ExportTable is the type they share.
See also
Section titled “See also”- Cell selection and clipboard: ⌘C on a range copies cells without headers.
- Columns: the formatting options the export reuses.
- Internationalisation:
localeand the menu labels.