Client-side Excel export#

When export is bounded (one page of grid data, already loaded) and server-side ExcelHandler / CsvHandler would be overkill, build the workbook in the browser using the xlsx library.

Install#

npm install xlsx file-saver
npm install -D @types/file-saver

Export utility#

// src/utils/ExcelExport.ts
import * as XLSX from 'xlsx';
import { saveAs } from 'file-saver';

export interface ExportColumn {
  key:   string;     // attribute name on the row object
  label: string;     // column header in the workbook
}

export function exportToExcel(
  rows:     Record<string, any>[],
  columns:  ExportColumn[],
  fileName: string = 'export',
  options?: { sheetName?: string; includeTotal?: string[] }
) {
  // Map rows to labelled columns
  const data = rows.map(row =>
    Object.fromEntries(columns.map(c => [c.label, row[c.key] ?? '']))
  );

  // Optional total row
  if (options?.includeTotal) {
    const totals: Record<string, any> = {};
    columns.forEach(c => {
      if (options.includeTotal!.includes(c.key)) {
        totals[c.label] = rows.reduce(
          (sum, r) => sum + (Number(r[c.key]) || 0), 0
        );
      } else {
        totals[c.label] = c === columns[0] ? 'TOTAL' : '';
      }
    });
    data.push(totals);
  }

  const ws = XLSX.utils.json_to_sheet(data);
  const wb = XLSX.utils.book_new();
  XLSX.utils.book_append_sheet(wb, ws, options?.sheetName ?? 'Sheet1');
  const buf = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
  saveAs(new Blob([buf], { type: 'application/octet-stream' }), `${fileName}.xlsx`);
}

Using it from a grid toolbar#

import { exportToExcel, type ExportColumn } from '../../utils/ExcelExport';

const exportColumns: ExportColumn[] = [
  { key: 'loginName',      label: 'Email' },
  { key: 'firstName',      label: 'First Name' },
  { key: 'lastName',       label: 'Last Name' },
  { key: 'departmentCode', label: 'Department' },
  { key: 'status',         label: 'Status' },
];

// Inside your custom DataGridControls:
const handleExport = () => {
  const rows = o.queryRef?.current?.getCurrentData() ?? [];
  exportToExcel(rows, exportColumns, 'employees', {
    sheetName: 'Employees',
  });
};

<Button onClick={handleExport}>Export Excel</Button>

When to use client-side vs. server-side export#

Client-side (xlsx + file-saver) Server-side (CsvHandler / ExcelHandler)
Already-loaded rows (one page) Full result set across all pages
No server round-trip Streams from the database — handles millions of rows
Browser memory is the limit Server memory scales independently
Quick to implement Needs a dedicated handler + @CrudMapping

Use both in the same app: the grid toolbar’s “Export current page” calls the client-side utility; a separate “Export all” button points at the server-side handler URL.

See also: Bulk exports, CsvHandler, ExcelHandler.