Skip to content

Excel Export

RevoGrid Pro exports grid data to .xlsx workbooks through ExportExcelPlugin. The same plugin also keeps the historical CSV drag-and-drop import path, so existing integrations can keep using the ExportExcelPlugin, grid.exportExcel, and export-excel event names.

Source code
TypeScriptts
// src/components/export-excel/ExportExcel.ts

import { defineCustomElements } from '@revolist/revogrid/loader';
defineCustomElements();

import { currentTheme, useRandomData } from '../composables/useRandomData';
import './export-excel.scss';
import {
  EXCEL_DEMO_ROW_COUNT,
  applyExcelDemoGridSettings,
  createDefaultExcelDemoExportOptions,
  createDefaultExcelDemoGridOptions,
  createExcelDemoCellMerge,
  createExcelDemoColumnTypes,
  createExcelDemoColumns,
  createExcelDemoExportConfig,
  createExcelDemoPinnedBottomSource,
  createExcelDemoPinnedTopSource,
  createExcelDemoRows,
  type ExcelDemoExportOptions,
  type ExcelDemoGridOptions,
} from './export-excel-demo-options';
import {
  EXCEL_DEMO_EXPORT_EVENT,
  EXCEL_DEMO_OPTIONS_CHANGE_EVENT,
  createExcelDemoToolbar,
  type ExcelDemoExportEvent,
  type ExcelDemoOptionsChangeEvent,
} from './export-excel-demo-toolbar';

const { createRandomData } = useRandomData();
const { isDark } = currentTheme();

import {
  ColumnStretchPlugin,
  CellMergePlugin,
  ExportExcelPlugin,
  FormulaPlugin,
  RowOddPlugin,
} from '@revolist/revogrid-pro';
export function load(parentSelector: string) {
  const parent = document.querySelector(parentSelector);
  if (!parent) {
    return;
  }

  const container = document.createElement('div');
  container.className = `export-excel-demo grow ${gridOptionsClass(isDark())}`;

  let exportOptions = createDefaultExcelDemoExportOptions();
  let gridOptions = createDefaultExcelDemoGridOptions(isDark());
  const mergeRenderedGridOptions = (
    options: { exportOptions: ExcelDemoExportOptions; gridOptions: ExcelDemoGridOptions },
  ) => ({
    ...options.gridOptions,
    ...options.exportOptions,
  });
  const toolbar = createExcelDemoToolbar({ exportOptions, gridOptions });

  const grid = document.createElement('revo-grid');
  grid.className = 'export-excel-grid cell-border';
  grid.columns = createExcelDemoColumns(gridOptions);
  grid.columnTypes = createExcelDemoColumnTypes();
  grid.cellMerge = createExcelDemoCellMerge(gridOptions);
  grid.pinnedTopSource = createExcelDemoPinnedTopSource(gridOptions);
  grid.pinnedBottomSource = createExcelDemoPinnedBottomSource(gridOptions);
  grid.stretch = 'all';
  // Define plugin
  grid.plugins = [ExportExcelPlugin, FormulaPlugin, RowOddPlugin, ColumnStretchPlugin, CellMergePlugin];

  toolbar.addEventListener(EXCEL_DEMO_OPTIONS_CHANGE_EVENT, (event) => {
    ({ exportOptions, gridOptions } = (event as ExcelDemoOptionsChangeEvent).detail);
    container.classList.toggle('is-dark', gridOptions.theme === 'darkMaterial');
    applyExcelDemoGridSettings(grid, mergeRenderedGridOptions({ exportOptions, gridOptions }));
  });

  toolbar.addEventListener(EXCEL_DEMO_EXPORT_EVENT, async (event) => {
    ({ exportOptions, gridOptions } = (event as ExcelDemoExportEvent).detail);
    toolbar.exporting = true;
    try {
      const plugins = await grid.getPlugins();
      const plugin = plugins.find(p => p instanceof ExportExcelPlugin) as ExportExcelPlugin;
      await plugin?.export(createExcelDemoExportConfig(exportOptions));
    } finally {
      toolbar.exporting = false;
    }

    // Or dispatch the public event with the same provider options.
    // dispatch(grid, 'export-excel', createExcelDemoExportConfig(exportOptions));
  });

  grid.theme = gridOptions.theme;
  grid.hideAttribution = true;
  applyExcelDemoGridSettings(grid, mergeRenderedGridOptions({ exportOptions, gridOptions }));
  container.append(toolbar, grid);
  parent.appendChild(container);
  grid.source = createExcelDemoRows(createRandomData(EXCEL_DEMO_ROW_COUNT));

  return () => container.remove();
}

function gridOptionsClass(isDarkTheme: boolean) {
  return isDarkTheme ? 'is-dark' : '';
}
Vuevue
// src/components/export/ExportExcel.vue
<template>
  <div :class="['export-excel-demo', { 'is-dark': gridOptions.theme === 'darkMaterial' }]">
    <export-excel-demo-toolbar
      ref="toolbar"
      @excel-demo-options-change="updateToolbarOptions"
      @excel-demo-export="exportToExcel"
    />
    <VGrid
      ref="grid"
      class="export-excel-grid cell-border"
      :theme="gridOptions.theme"
      :columns="columns"
      :column-types="columnTypes"
      :cell-merge.prop="cellMerge"
      :source="rows"
      :plugins="plugins"
      stretch="all"
      :pinned-top-source="pinnedTopSource"
      :pinned-bottom-source="pinnedBottomSource"
      hide-attribution
    />
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { currentThemeVue, useRandomData } from '../composables/useRandomData'
import { VGrid } from '@revolist/vue3-datagrid'
import { ExportExcelPlugin, ColumnStretchPlugin, FormulaPlugin, RowOddPlugin, CellMergePlugin } from '@revolist/revogrid-pro'
import './export-excel.scss'
import {
  EXCEL_DEMO_ROW_COUNT,
  applyExcelDemoGridSettings,
  createDefaultExcelDemoExportOptions,
  createDefaultExcelDemoGridOptions,
  createExcelDemoCellMerge,
  createExcelDemoColumnTypes,
  createExcelDemoColumns,
  createExcelDemoExportConfig,
  createExcelDemoPinnedBottomSource,
  createExcelDemoPinnedTopSource,
  createExcelDemoRows,
} from './export-excel-demo-options'
import {
  defineExcelDemoToolbarElement,
  type ExcelDemoExportEvent,
  type ExcelDemoOptionsChangeEvent,
  type ExcelDemoToolbarElement,
} from './export-excel-demo-toolbar'

defineExcelDemoToolbarElement()

const { createRandomData } = useRandomData()
const { isDark } = currentThemeVue()

const grid = ref<{ $el: HTMLRevoGridElement } | null>(null);
const toolbar = ref<ExcelDemoToolbarElement | null>(null);
const exporting = ref(false)

const exportOptions = reactive(createDefaultExcelDemoExportOptions())
const gridOptions = reactive(createDefaultExcelDemoGridOptions(isDark.value))
const renderedGridOptions = computed(() => ({ ...gridOptions, ...exportOptions }))
const columns = computed(() => createExcelDemoColumns(renderedGridOptions.value))
const cellMerge = computed(() => createExcelDemoCellMerge(renderedGridOptions.value))
const columnTypes = createExcelDemoColumnTypes()

const plugins = [ExportExcelPlugin, ColumnStretchPlugin, FormulaPlugin, RowOddPlugin, CellMergePlugin]

const rows = ref(createExcelDemoRows(createRandomData(EXCEL_DEMO_ROW_COUNT)))
const pinnedTopSource = computed(() => createExcelDemoPinnedTopSource(renderedGridOptions.value))
const pinnedBottomSource = computed(() => createExcelDemoPinnedBottomSource(renderedGridOptions.value))

const syncRenderedGridSettings = () => {
  if (grid.value) {
    applyExcelDemoGridSettings(grid.value.$el, renderedGridOptions.value)
  }
}

const syncToolbar = () => {
  if (toolbar.value) {
    toolbar.value.options = {
      exportOptions: { ...exportOptions },
      gridOptions: { ...gridOptions },
    }
    toolbar.value.exporting = exporting.value
  }
}

onMounted(() => {
  syncRenderedGridSettings()
  syncToolbar()
})
watch(renderedGridOptions, () => {
  syncRenderedGridSettings()
  syncToolbar()
}, { flush: 'post' })
watch(exporting, syncToolbar)

const updateToolbarOptions = (event: ExcelDemoOptionsChangeEvent) => {
  Object.assign(exportOptions, event.detail.exportOptions)
  Object.assign(gridOptions, event.detail.gridOptions)
}

const exportToExcel = async (event: ExcelDemoExportEvent) => {
  if (grid.value) {
    try {
      exporting.value = true
      const plugins = await grid.value.$el.getPlugins()
      const exportPlugin = plugins.find(
        (plugin) => plugin instanceof ExportExcelPlugin
      ) as ExportExcelPlugin
      await exportPlugin?.export(createExcelDemoExportConfig(event.detail.exportOptions))
    } finally {
      exporting.value = false
    }
  }
}
</script>
Reacttsx
// src/components/export/ExportExcel.tsx

import React, { useEffect, useState, useMemo, useRef } from 'react';
import { RevoGrid, type DataType } from '@revolist/react-datagrid';
import {
  CellMergePlugin,
  ColumnStretchPlugin,
  ExportExcelPlugin,
  FormulaPlugin,
  RowOddPlugin,
} from '@revolist/revogrid-pro';
import { useRandomData, currentTheme } from '../composables/useRandomData';
import './export-excel.scss';
import {
  EXCEL_DEMO_ROW_COUNT,
  applyExcelDemoGridSettings,
  createDefaultExcelDemoExportOptions,
  createDefaultExcelDemoGridOptions,
  createExcelDemoCellMerge,
  createExcelDemoColumnTypes,
  createExcelDemoColumns,
  createExcelDemoExportConfig,
  createExcelDemoPinnedBottomSource,
  createExcelDemoPinnedTopSource,
  createExcelDemoRows,
  type ExcelDemoExportOptions,
  type ExcelDemoGridOptions,
} from './export-excel-demo-options';
import {
  EXCEL_DEMO_EXPORT_EVENT,
  EXCEL_DEMO_OPTIONS_CHANGE_EVENT,
  EXCEL_DEMO_TOOLBAR_TAG,
  defineExcelDemoToolbarElement,
  type ExcelDemoExportEvent,
  type ExcelDemoOptionsChangeEvent,
  type ExcelDemoToolbarElement,
} from './export-excel-demo-toolbar';

const { isDark } = currentTheme();
const { createRandomData } = useRandomData();

function ExportExcel() {
  const gridRef = useRef<HTMLRevoGridElement>(null);
  const toolbarRef = useRef<ExcelDemoToolbarElement>(null);
  const [source] = useState<DataType[]>(
    () => createExcelDemoRows(createRandomData(EXCEL_DEMO_ROW_COUNT)),
  );
  const [exportOptions, setExportOptions] = useState<ExcelDemoExportOptions>(() =>
    createDefaultExcelDemoExportOptions()
  );
  const [gridOptions, setGridOptions] = useState<ExcelDemoGridOptions>(() =>
    createDefaultExcelDemoGridOptions(isDark())
  );
  const [exporting, setExporting] = useState(false);

  const renderedGridOptions = useMemo(
    () => ({ ...gridOptions, ...exportOptions }),
    [exportOptions, gridOptions],
  );
  const columns = useMemo(() => createExcelDemoColumns(renderedGridOptions), [renderedGridOptions]);
  const cellMerge = useMemo(() => createExcelDemoCellMerge(renderedGridOptions), [renderedGridOptions]);
  const columnTypes = useMemo(() => createExcelDemoColumnTypes(), []);
  const pinnedTopSource = useMemo(
    () => createExcelDemoPinnedTopSource(renderedGridOptions),
    [renderedGridOptions],
  );
  const pinnedBottomSource = useMemo(
    () => createExcelDemoPinnedBottomSource(renderedGridOptions),
    [renderedGridOptions],
  );
  const plugins = useMemo(
    () => [ExportExcelPlugin, FormulaPlugin, RowOddPlugin, ColumnStretchPlugin, CellMergePlugin],
    [],
  );

  useEffect(() => {
    if (gridRef.current) {
      applyExcelDemoGridSettings(gridRef.current, renderedGridOptions);
    }
  }, [renderedGridOptions]);

  useEffect(() => {
    defineExcelDemoToolbarElement();
  }, []);

  useEffect(() => {
    if (toolbarRef.current) {
      toolbarRef.current.options = { exportOptions, gridOptions };
    }
  }, [exportOptions, gridOptions]);

  useEffect(() => {
    if (toolbarRef.current) {
      toolbarRef.current.exporting = exporting;
    }
  }, [exporting]);

  useEffect(() => {
    const toolbar = toolbarRef.current;
    if (!toolbar) {
      return;
    }

    const handleOptionsChange = (event: Event) => {
      const { exportOptions: nextExportOptions, gridOptions: nextGridOptions } = (
        event as ExcelDemoOptionsChangeEvent
      ).detail;

      setExportOptions(nextExportOptions);
      setGridOptions(nextGridOptions);
    };

    const handleExport = (event: Event) => {
      void exportToExcel((event as ExcelDemoExportEvent).detail.exportOptions);
    };

    toolbar.addEventListener(EXCEL_DEMO_OPTIONS_CHANGE_EVENT, handleOptionsChange);
    toolbar.addEventListener(EXCEL_DEMO_EXPORT_EVENT, handleExport);

    return () => {
      toolbar.removeEventListener(EXCEL_DEMO_OPTIONS_CHANGE_EVENT, handleOptionsChange);
      toolbar.removeEventListener(EXCEL_DEMO_EXPORT_EVENT, handleExport);
    };
  }, []);

  const exportToExcel = async (options: ExcelDemoExportOptions) => {
    const grid = gridRef.current;
    if (grid) {
      try {
        setExporting(true);
        const gridPlugins = await grid.getPlugins();
        const exportPlugin = gridPlugins.find(
          (plugin) => plugin instanceof ExportExcelPlugin
        ) as ExportExcelPlugin;
        await exportPlugin?.export(createExcelDemoExportConfig(options));
      } finally {
        setExporting(false);
      }
    }
  };

  return (
    <div className={`export-excel-demo ${gridOptions.theme === 'darkMaterial' ? 'is-dark' : ''}`}>
      {React.createElement(EXCEL_DEMO_TOOLBAR_TAG, { ref: toolbarRef })}
      <RevoGrid
        ref={gridRef}
        columns={columns}
        columnTypes={columnTypes}
        cellMerge={cellMerge}
        source={source}
        pinnedTopSource={pinnedTopSource}
        pinnedBottomSource={pinnedBottomSource}
        hide-attribution
        theme={gridOptions.theme}
        plugins={plugins}
        className="export-excel-grid cell-border"
        stretch="all"
      />
    </div>
  );
}

export default ExportExcel;
Angularts
// src/components/export-excel/ExportExcelAngular.ts

import {
  AfterViewInit,
  CUSTOM_ELEMENTS_SCHEMA,
  Component,
  ElementRef,
  ViewEncapsulation,
  ViewChild,
} from '@angular/core';
import { RevoGrid } from '@revolist/angular-datagrid';
import {
  CellMergePlugin,
  ColumnStretchPlugin,
  ExportExcelPlugin,
  FormulaPlugin,
  RowOddPlugin,
} from '@revolist/revogrid-pro';
import { useRandomData, currentTheme } from '../composables/useRandomData';
import {
  EXCEL_DEMO_ROW_COUNT,
  applyExcelDemoGridSettings,
  createDefaultExcelDemoExportOptions,
  createDefaultExcelDemoGridOptions,
  createExcelDemoCellMerge,
  createExcelDemoColumnTypes,
  createExcelDemoColumns,
  createExcelDemoExportConfig,
  createExcelDemoPinnedBottomSource,
  createExcelDemoPinnedTopSource,
  createExcelDemoRows,
  type ExcelDemoExportOptions,
  type ExcelDemoGridOptions,
} from './export-excel-demo-options';
import {
  defineExcelDemoToolbarElement,
  type ExcelDemoExportEvent,
  type ExcelDemoOptionsChangeEvent,
  type ExcelDemoToolbarElement,
} from './export-excel-demo-toolbar';

defineExcelDemoToolbarElement();

@Component({
  selector: 'export-excel-grid',
  standalone: true,
  imports: [RevoGrid],
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  template: `
    <div class="export-excel-demo" [class.is-dark]="gridOptions.theme === 'darkMaterial'">
      <export-excel-demo-toolbar
        #toolbarRef
        (excel-demo-options-change)="setToolbarOptions($event)"
        (excel-demo-export)="exportExcel($event)"
      ></export-excel-demo-toolbar>
      <revo-grid
        #gridRef
        [columns]="columns"
        [columnTypes]="columnTypes"
        [cellMerge]="cellMerge"
        [source]="source"
        [pinnedTopSource]="pinnedTopSource"
        [pinnedBottomSource]="pinnedBottomSource"
        [hideAttribution]="true"
        [theme]="gridOptions.theme"
        [plugins]="plugins"
        [stretch]="'all'"
        class="export-excel-grid cell-border"
      ></revo-grid>
    </div>`,
  styleUrls: ['./export-excel.scss'],
  encapsulation: ViewEncapsulation.None,
})
export class ExportExcelGridComponent implements AfterViewInit {
  @ViewChild('gridRef', { static: true })
  gridRef!: HTMLRevoGridElement;
  @ViewChild('toolbarRef', { static: true, read: ElementRef })
  toolbarRef!: ElementRef<ExcelDemoToolbarElement>;
  readonly source = createExcelDemoRows(
    useRandomData().createRandomData(EXCEL_DEMO_ROW_COUNT),
  );
  exportOptions = createDefaultExcelDemoExportOptions();
  gridOptions = createDefaultExcelDemoGridOptions(currentTheme().isDark());
  columns = createExcelDemoColumns(this.gridOptions);
  cellMerge = createExcelDemoCellMerge(this.gridOptions);
  columnTypes = createExcelDemoColumnTypes();
  pinnedTopSource = createExcelDemoPinnedTopSource(this.gridOptions);
  pinnedBottomSource = createExcelDemoPinnedBottomSource(this.gridOptions);
  exporting = false;

  plugins = [ExportExcelPlugin, FormulaPlugin, RowOddPlugin, ColumnStretchPlugin, CellMergePlugin];

  private get renderedGridOptions() {
    return {
      ...this.gridOptions,
      ...this.exportOptions,
    };
  }

  setToolbarOptions(event: ExcelDemoOptionsChangeEvent) {
    this.exportOptions = event.detail.exportOptions;
    this.gridOptions = event.detail.gridOptions;
    this.updateGridDerivedSettings();
  }

  updateGridDerivedSettings() {
    this.columns = createExcelDemoColumns(this.gridOptions);
    this.cellMerge = createExcelDemoCellMerge(this.gridOptions);
    this.pinnedTopSource = createExcelDemoPinnedTopSource(this.gridOptions);
    this.pinnedBottomSource = createExcelDemoPinnedBottomSource(this.gridOptions);
    this.updateRenderedGridSettings();
  }

  updateRenderedGridSettings() {
    if (this.gridRef) {
      applyExcelDemoGridSettings(this.gridRef, this.renderedGridOptions);
    }
  }

  updateToolbarSettings() {
    if (this.toolbarRef) {
      this.toolbarRef.nativeElement.options = {
        exportOptions: this.exportOptions,
        gridOptions: this.gridOptions,
      };
      this.toolbarRef.nativeElement.exporting = this.exporting;
    }
  }

  ngAfterViewInit() {
    this.updateRenderedGridSettings();
    this.updateToolbarSettings();
  }

  async exportExcel(event: ExcelDemoExportEvent) {
    try {
      this.exporting = true;
      this.updateToolbarSettings();
      const plugins = await this.gridRef?.getPlugins();
      const plugin = plugins.find(
        (p) => p instanceof ExportExcelPlugin,
      ) as ExportExcelPlugin;
      await plugin?.export(createExcelDemoExportConfig(event.detail.exportOptions));
    } finally {
      this.exporting = false;
      this.updateToolbarSettings();
    }

    // Alternatively, dispatch event
    // dispatch(grid, 'export-excel', createExcelDemoExportConfig(this.exportOptions));
  }
}

Attach ExportExcelPlugin with your grid plugins:

import { ExportExcelPlugin } from '@revolist/revogrid-pro';
grid.plugins = [ExportExcelPlugin];

Trigger export through the event API:

grid.dispatchEvent(new CustomEvent('export-excel', {
detail: {
workbookName: 'orders.xlsx',
sheetName: 'Orders',
},
}));

Or call the plugin instance directly:

const plugins = await grid.getPlugins();
const excel = plugins.find((plugin) => plugin instanceof ExportExcelPlugin);
await excel?.export({
workbookName: 'orders.xlsx',
sheetName: 'Orders',
});
  • The bundled provider is write-excel-file and exports .xlsx files only.
  • Extensionless workbookName values receive .xlsx.
  • sheetName is normalized for Excel worksheet rules.
  • Export reads grid data stores, not rendered DOM rows, so virtualized offscreen rows are included.
  • Body export follows the store’s visible row projection, preserving current sort and filter order and the expanded/collapsed grouping view.
  • Row order is pinned top rows, body rows, then pinned bottom rows.
  • Columns follow providers.column.getColumns('all').
  • Grid-derived widths and freeze panes are inferred by default from column sizes, colPinStart columns, and pinned-top rows.

When DataGridFormattingPlugin and ExportExcelPlugin are active together, Format Cells settings are transferred automatically to the workbook. This is also the normal setup when DataGridContextMenuPlugin supplies Format Cells:

import {
DataGridContextMenuPlugin,
ExportExcelPlugin,
} from '@revolist/revogrid-pro';
grid.plugins = [DataGridContextMenuPlugin, ExportExcelPlugin];

The export keeps raw numeric and date values as workbook values and attaches native Excel number formats for Number, Currency, Accounting, Percentage, Scientific, Date, Time, Date & time, and Plain text presets. Excel can therefore continue to calculate, sort, and edit those cells. Font family and size, emphasis, text and fill colors, alignment, wrapping, and borders are mapped to workbook cell styles.

Row grouping is exported from the visible provider projection. Group records become merged, indented group-heading rows, while expanded descendants retain their displayed order and descendants hidden by a collapsed group are omitted. context.exportMetadata.rowGrouping also exposes each exported group row’s worksheet row, depth, label, and expanded state to custom providers.

  • Export Providers explains provider precedence, default .xlsx limits, providerOptions, deriveGridOptions, optional ExcelJS and SheetJS adapters, workbook-module adapters, and custom providers.
  • Cell Formatting explains automatic Format Cells transfer, column.excelExport, formula/date/numeral/select helpers, grouped rows and headers, cell merge export support, and the provider context matrix.
  • CSV Import explains CSV-only drag/drop import, allowDrag, allowedExtensions, skipColumnGeneration, events, and plugin import helpers.
  • Migrating to v2.0 explains the SheetJS removal, CSV-only default import, app-owned XLSX import, and provider migration paths as a breaking change from 2.0.8.