Skip to main content

CSV and Excel export

Export the complete view, a selected range, or selected columns from the toolbar, menus, or API.

Loading interactive example…
React
<Grid
rowData={rows}
columnDefs={columns}
rowNumbers
toolbar={{ export: true }}
onGridReady={(api) => {
// api.exportDataAsExcel({ scope: "selection" });
}}
/>

Output fidelity​

Excel output is written without runtime dependencies and includes hierarchical headers, cell spans, full-width row merges, pinned panes, typed number/date/ currency values, and application-pinned rows. Aggregate footers become live formulas when an equivalent Excel function exists.

Date cells are written on the local calendar, the day the cell shows. CSV writes a date column's formatted text (its format, YYYY-MM-DD by default); when the column's formatter leaves the value unchanged, CSV falls back to the local YYYY-MM-DD, adding a zone-less local THH:mm:ss only when the value has a time of day. It never writes a UTC timestamp, which would move a local-midnight date to the previous day east of Greenwich. Excel receives a real date, and date-only text such as "2026-03-01" is read as that local day.

Grouped export supports a tree with outline levels and per-group subtotals, or a flat leaf mode with full group paths. Selection export prunes the group tree to relevant rows.

On the server-side row model an export holds the rows the client has fetched, in the shape on screen, and its aggregate cells are the grid's displayed numbers written as values rather than formulas.

Use getDataAsCsv() or getDataAsExcel() when the application needs bytes without triggering a browser download—for example, to upload or encrypt a file.

Enable export commands​

const options = {
allowExportAsCSV: true,
allowExportAsExcel: true,
toolbar: { export: true },
} satisfies GridOptions;

Export is also available from column and body context menus.

Download CSV or Excel​

api.exportDataAsCsv({ fileName: "orders.csv" });
api.exportDataAsExcel({ fileName: "orders.xlsx" });

CSV options​

These options shape the CSV text only; the Excel export ignores them. They apply to every CSV path: the download, getDataAsCsv() (which returns exactly the text the download holds), body and pinned rows, and grouped grids, whose CSV is the flat list of leaf rows whatever the groupMode.

OptionDefaultEffect
columnSeparator","Text between fields. ";" suits Excel in locales whose decimal mark is a comma; "\t" writes tab-separated values. A field containing the separator is quoted.
lineEnding"\n"Text between rows. RFC 4180 specifies "\r\n", which some strict consumers require.
includeBomfalseStart the file with a UTF-8 byte order mark. Without it Excel reads the file in the system's legacy code page and garbles non-ASCII text.
suppressQuotesfalseNever quote fields; the caller guarantees no field contains the separator, a quote or a line break.
processCellForCsv(params) => string | undefined: replace a data cell's text. params is { value, formattedValue, data, rowIndex, rowType, column }, the same shape processCellForExcel receives; rowType is "body", "pinnedTop" or "pinnedBottom", and rowIndex counts from 0 within it. Return undefined to keep the grid's text.
processHeaderForCsv({ column, label }) => string | undefined: replace a header label. On a grouped header, column is the column group for the group row's labels.
prependContent / appendContentContent before the header or after the last row. A string is written verbatim as its own line(s); a 2-D array is written as CSV rows with the export's separator and quoting.
// Excel in a comma-decimal locale, with accented names intact and card numbers masked.
api.exportDataAsCsv({
fileName: "customers.csv",
columnSeparator: ";",
includeBom: true,
prependContent: [["Customer export", new Date().toLocaleDateString()]],
processCellForCsv: ({ column, formattedValue }) =>
column.colId === "cardNumber" ? `**** ${formattedValue.slice(-4)}` : undefined,
});

Export a range or selected columns​

api.exportDataAsCsv({ scope: "selection" });

api.exportDataAsExcel({
scope: "selectedColumns",
columnIds: ["orderNo", "customer", "total"],
includeHeaders: true,
});

Grouped export​

api.exportDataAsExcel({ groupMode: "tree" }); // group headers + subtotals
api.exportDataAsExcel({ groupMode: "leaves" }); // flat leaves with group paths

CSV and Excel include application-pinned top and bottom rows around the body.

Group footer rows (groupTotalRow, grandTotalRow) are not exported as rows: the tree-mode Excel export already writes a SUBTOTAL per group (and, while aggregates show, a grand-total footer), so the file is the same with or without them, and CSV writes leaf rows only.

Pivot export​

While pivot mode is on, the same commands export the pivot table itself: the generated nested header over one row per group node (every level, expanded or not, indented under the group column), with real numbers in the aggregate cells. Pivot exports are whole-table by design — the range and selected-column scopes are ignored — and the default filename becomes grid-pivot.csv / grid-pivot.xlsx. Turn pivot mode off to export the source rows instead.

Formula injection​

A spreadsheet that opens a CSV evaluates any text cell starting with =, +, -, @, a tab or a carriage return as a formula. The CSV export therefore writes such text with a leading single quote (=1+1 becomes '=1+1), in body cells, group and full-width labels, and header labels. Cells written as numbers (a numeric value, or a numeric value in a NUMBER / CURRENCY column) are left as they are, so -5 stays -5. The guard is always on, whatever the separator and even with suppressQuotes. Text returned by processCellForCsv or processHeaderForCsv, and the cells of a 2-D prependContent / appendContent, are guarded too; a cell result or extra-content cell that is a plain number such as -5 is left as it is. A string prependContent / appendContent is the application's own text and is written verbatim.

The Excel export needs no guard: text is written as string cells, which Excel never evaluates. The only formula cells are the aggregate and subtotal formulas the exporter writes itself.

Get bytes without downloading​

const csv = api.getDataAsCsv({ scope: "all" });
await fetch("/exports/orders.csv", { method: "PUT", body: csv });

const bytes = await api.getDataAsExcel();
const workbook = new Blob([bytes], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});