Skip to content

Excel Import and Export Libraries

RevoGrid Pro includes an Excel export plugin, but most production apps still need to choose how uploaded workbook files are parsed, validated, normalized, and streamed into the grid.

Use this guide when your workflow is:

  1. User uploads an Excel file.
  2. Your app parses .xlsx data.
  3. Your app converts the first sheet, selected sheet, or validated sheet into JSON.
  4. RevoGrid receives normalized columns and source data.
GoalRecommended libraryWhy
Fast .xlsx upload to grid JSONSheetJSBroad format support, direct sheet_to_json() conversion, strong compatibility with real-world workbooks.
Full workbook editing or styled report generationExcelJSGood API for worksheets, rows, formatting, formulas, tables, merged cells, and generated reports.
Simple validated upload formsread-excel-fileSmall API surface with schema-based parsing and validation.
Default RevoGrid Pro .xlsx exportwrite-excel-fileLightweight writer used by the bundled export provider.
Huge browser importsSheetJS or read-excel-file in a Web WorkerKeep parse work away from the UI thread and load rows into RevoGrid incrementally.
Backend import/export processingSheetJS or ExcelJSPick SheetJS for broad import compatibility; pick ExcelJS when workbook editing and formatting matter more.

SheetJS is the strongest general-purpose choice for Excel upload workflows. It works in the browser and Node.js, reads workbook bytes from an ArrayBuffer, supports common spreadsheet formats, and converts worksheets directly into JSON.

Use it when the import goal is “Excel file in, JSON rows out”.

Terminal window
pnpm add xlsx
import * as XLSX from 'xlsx';
import type { ColumnRegular, DataType } from '@revolist/revogrid';
export async function parseSheetJsFile(file: File): Promise<{
columns: ColumnRegular[];
rows: DataType[];
}> {
const data = await file.arrayBuffer();
const workbook = XLSX.read(data, { cellDates: true });
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
const rows = XLSX.utils.sheet_to_json<Record<string, unknown>>(worksheet, {
defval: '',
});
const columns = Object.keys(rows[0] ?? {}).map((key) => ({
prop: key,
name: key,
}));
return { columns, rows };
}
const { columns, rows } = await parseSheetJsFile(file);
grid.columns = columns;
grid.source = rows;

SheetJS is usually the best RevoGrid import fit for admin panels, ERP screens, analytics tools, and customer-provided enterprise spreadsheets where file shape is not perfectly controlled.

The tradeoff is that parsing large files on the main browser thread can freeze the UI. For large files, parse in a Web Worker and only send normalized data back to the page.

xlsx-import.worker.ts
import * as XLSX from 'xlsx';
self.onmessage = async (event: MessageEvent<File>) => {
const workbook = XLSX.read(await event.data.arrayBuffer(), { cellDates: true });
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
const rows = XLSX.utils.sheet_to_json<Record<string, unknown>>(worksheet, {
defval: '',
});
const columns = Object.keys(rows[0] ?? {}).map((key) => ({
prop: key,
name: key,
}));
self.postMessage({ columns, rows });
};
import-ui.ts
const worker = new Worker(new URL('./xlsx-import.worker.ts', import.meta.url), {
type: 'module',
});
worker.onmessage = ({ data }) => {
grid.columns = data.columns;
grid.source = data.rows;
};
worker.postMessage(file);

ExcelJS is better when you need to read, edit, preserve, or generate richer workbook structures. It is a good fit for financial reports, styled exports, workbook templates, formulas, tables, and merged-cell workflows.

Use it when the app needs workbook manipulation, not only raw import speed.

Terminal window
pnpm add exceljs
import ExcelJS from 'exceljs';
import type { ColumnRegular, DataType } from '@revolist/revogrid';
export async function parseExcelJsFile(file: File): Promise<{
columns: ColumnRegular[];
rows: DataType[];
}> {
const workbook = new ExcelJS.Workbook();
await workbook.xlsx.load(await file.arrayBuffer());
const worksheet = workbook.worksheets[0];
const headerRow = worksheet.getRow(1);
const headerValues = Array.isArray(headerRow.values)
? headerRow.values.slice(1)
: Object.values(headerRow.values);
const headers = headerValues
.map((value) => String(value ?? '').trim())
.filter(Boolean);
const columns = headers.map((header) => ({
prop: header,
name: header,
}));
const rows: DataType[] = [];
worksheet.eachRow((row, rowNumber) => {
if (rowNumber === 1) {
return;
}
const model: DataType = {};
headers.forEach((header, index) => {
model[header] = row.getCell(index + 1).value ?? '';
});
rows.push(model);
});
return { columns, rows };
}

ExcelJS can also be used as an optional RevoGrid export provider when you need workbook features beyond the bundled provider.

import ExcelJS from 'exceljs';
import { createExcelJsExcelExportProvider } from '@revolist/revogrid-pro';
grid.exportExcel = {
exportProvider: createExcelJsExcelExportProvider(ExcelJS),
};

The tradeoff is size and speed. ExcelJS is usually heavier than SheetJS for pure “parse and convert to JSON” imports.

read-excel-file is the best fit when uploads are simple and validation matters more than workbook feature coverage. It reads .xlsx files in the browser or Node.js and can map columns into typed JSON with a schema.

Terminal window
pnpm add read-excel-file
import readXlsxFile from 'read-excel-file';
const schema = {
name: {
column: 'Name',
type: String,
required: true,
},
age: {
column: 'Age',
type: Number,
},
};
const { rows, errors } = await readXlsxFile(file, { schema });
if (errors.length) {
throw new Error(`Invalid spreadsheet: ${errors[0].error}`);
}
grid.columns = [
{ prop: 'name', name: 'Name' },
{ prop: 'age', name: 'Age' },
];
grid.source = rows;

This is a strong choice for import forms, onboarding flows, and ETL-style screens where the workbook is really a typed table.

The tradeoff is that it is not intended to be a full workbook manipulation layer. If users upload complex workbooks with formulas, styles, merged headers, comments, or multiple business sheets, use SheetJS or ExcelJS instead.

write-excel-file is a lightweight .xlsx writer for browser and Node.js. It is also the default writer behind RevoGrid Pro’s bundled Excel export provider.

For most RevoGrid exports, use ExportExcelPlugin directly:

import { ExportExcelPlugin } from '@revolist/revogrid-pro';
grid.plugins = [ExportExcelPlugin];
const plugins = await grid.getPlugins();
await plugins.find((plugin) => plugin instanceof ExportExcelPlugin)?.export({
workbookName: 'orders.xlsx',
sheetName: 'Orders',
});

Use write-excel-file directly when you are exporting app data that does not need the grid export pipeline.

Terminal window
pnpm add write-excel-file
import writeXlsxFile from 'write-excel-file';
await writeXlsxFile(
[
[
{ value: 'Name', fontWeight: 'bold' },
{ value: 'Revenue', fontWeight: 'bold' },
],
[
{ value: 'North', type: String },
{ value: 12000, type: Number, format: '$#,##0' },
],
],
{
fileName: 'summary.xlsx',
sheet: 'Summary',
},
);

The tradeoff is intentional simplicity. If export needs template workbooks, charts, complex formulas, images, or heavy workbook editing, use ExcelJS or a custom provider.

For RevoGrid imports, keep the parser separate from the grid:

  1. Read the uploaded file.
  2. Parse workbook data with the selected library.
  3. Normalize headers into stable ColumnRegular objects.
  4. Normalize each row into a plain object keyed by column.prop.
  5. Validate and coerce values before assigning them to the grid.
  6. Assign grid.columns before grid.source.
type ImportedTable = {
columns: { prop: string; name: string }[];
rows: Record<string, unknown>[];
};
async function importWorkbook(file: File): Promise<ImportedTable> {
return parseSheetJsFile(file);
}
const imported = await importWorkbook(file);
grid.columns = imported.columns;
grid.source = imported.rows;

Avoid putting the raw workbook object into reactive application state. Workbooks can be large and contain circular or library-specific objects. Store the normalized rows, validation errors, and lightweight import metadata instead.

Browser-side Excel parsing becomes visible to users once files are large enough. The exact threshold depends on column count, formulas, styles, browser, device, and parser options, but imports around 100k+ rows are where main-thread parsing commonly starts to feel expensive.

For larger files:

  • Parse inside a Web Worker.
  • Limit the first preview to a small row count.
  • Normalize rows in chunks.
  • Keep the original workbook out of framework state.
  • Show validation errors separately from accepted rows.
  • Let RevoGrid render the normalized dataset with virtualization.

For very large or regulated imports, parse on the backend and send RevoGrid paged or streamed JSON instead of asking the browser to own the whole workbook lifecycle.

For most RevoGrid apps:

  • Use ExportExcelPlugin with the bundled write-excel-file provider for grid export.
  • Use SheetJS for flexible .xlsx upload and header inference.
  • Use read-excel-file for small validated upload forms.
  • Use ExcelJS when the workbook itself is the product: formatted reports, templates, formulas, tables, and workbook editing.

That gives the fastest path for common XLSX-to-grid imports while keeping advanced export and workbook-editing cases open through RevoGrid’s custom provider contract.