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
TypeScript ts
// 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' : '';
}
Vue vue
// 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>
React tsx
// 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;
Angular ts
// 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.
  • 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.
  • Export Providers explains provider precedence, default .xlsx limits, providerOptions, deriveGridOptions, optional ExcelJS and SheetJS adapters, workbook-module adapters, and custom providers.
  • Cell Formatting explains column.excelExport, formula/date/numeral/select helpers, grouped 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.