Excel
Import and Export from XLSX files
Use the @rowsncolumns/toolkit package to read, write and download Excel files from SheetData object
Import from Excel
Converts an excel file to sheets, sheetData which can be used in the CanvasGrid.
Note: To generate patches, undo/redo when an excel file is uploaded, use
onInsertFilefunction fromuseSpreadsheetStatehook
import { createRowDataFromExcelFile } from "@rowsncolumns/toolkit";
const App = () => {
const { onInsertFile } = useSpreadsheetState()
const handleChange = async (e) => {
const file = e.target.files[0]
// Option 1: To let useSpreadsheetState hook to manage collab, undo/redo state
onInsertFile(file, sheetId, activeCell)
// Option 2: To manually update state
const { sheets, sheetData, tables } = await createRowDataFromExcelFile(file)
onChange(sheets)
onChangSheetData(sheetData)
onChangeTables(tables)
// Call calculateNow to trigger a full-recalc if you are using
// useSpreadsheetState hook
}
return (
<div>
<input type="file" onChange={handleChange} />
</div>
)
}Importing large excel files
createRowDataFromExcelFile when run on the main thread can block UI interactions. You can use importExcelFile to import excel file using a web worker.
import { useSpreadsheetState } from "@rowsncolumns/spreadsheet-state";
import { useLoadingIndicator } from "@rowsncolumns/spreadsheet"
const App = () => {
const { importExcelFile } = useSpreadsheetState({ ... });
const [showLoader, hideLoader] = useLoadingIndicator();
return (
<input
type="file"
onChange={async (e) => {
const file = e.target.files?.[0];
if (file) {
showLoader("Parsing excel file");
await importExcelFile(file)
hideLoader();
}
}}
/>
)
}Read Excel in Node.js
The browser worker (libs/toolkit/excel-parser/worker.ts) demonstrates the low-level primitives that parse .xlsx files. You can reuse the same helpers on the server to read workbooks, stream rows, and fetch metadata such as conditional formatting, named ranges, fonts, drawings, charts, and data validations.
import { readFile } from "node:fs/promises";
import {
WorkBook,
readExcelFile,
getSheets,
processSheetData,
getConditionalFormatting,
getDrawings,
getCharts,
getDataValidations,
} from "@rowsncolumns/toolkit";
const workbook = new WorkBook();
export async function readExcelOnServer(filePath: string) {
const nodeBuffer = await readFile(filePath);
// Convert Node's Buffer into the ArrayBuffer expected by the parser
const arrayBuffer = nodeBuffer.buffer.slice(
nodeBuffer.byteOffset,
nodeBuffer.byteOffset + nodeBuffer.byteLength
);
const buffer = await readExcelFile(arrayBuffer);
workbook.load(buffer);
const [sheets, tables] = await getSheets(workbook);
const conditionalFormatting = await getConditionalFormatting(workbook);
const dataValidations = await getDataValidations(workbook);
const drawings = await getDrawings(workbook);
const charts = await getCharts(workbook);
for await (const chunk of processSheetData(workbook, 500, true)) {
console.log(
`Sheet ${chunk.sheetId} (${chunk.title}) -> ${chunk.rows.length} rows`,
"cellXfs registry size",
chunk.cellXfs.size
);
}
workbook.dispose();
return {
sheets,
tables,
conditionalFormatting,
dataValidations,
drawings,
charts,
};
}Key steps:
read the file with Node APIs (e.g.
fs.readFile, S3 SDK) to obtain aBufferconvert the
Bufferinto anArrayBufferbefore callingreadExcelFilereuse the worker helpers (
WorkBook,getSheets,getConditionalFormatting,getDrawings,getCharts,getDataValidations,processSheetData) to assemble everything you need server-side
Streaming rows with processSheetData
processSheetDataSignature:
processSheetData(workbook, chunkSize = 100, enableCellXfsRegistry = false);workbook: the sharedWorkBookinstance withload()already called.chunkSize: how many logical rows you want in each yielded chunk (mirrors the worker's paging behavior).enableCellXfsRegistry: set totrueif you need thecellXfsmap populated so you can reuse deduped cell formats outside the worker.
Result type (from libs/toolkit/excel-parser/helpers.ts):
type ProcessSheetDataResult = {
sheetId: number;
title: string;
currentSheetIndex: number;
totalSheets: number;
chunkIndex: number;
rows: RowDataWithValue<CellData | null | undefined>[];
rowIndices: number[];
startRowIndex: number;
endRowIndex: number;
totalRows: number;
isLastChunk: boolean;
cellXfs: WorkBook["cellXfs"];
};cellXfs is the same memoized format registry the worker uses (a map keyed by the serialized cell format) so you can reconcile styles outside the worker.
Example: accumulate totals per sheet while keeping the formatting registry alive for later use.
const totals = new Map<number, number>();
for await (const chunk of processSheetData(workbook, 250, true)) {
const sum = chunk.rows.reduce((acc, row) => {
const numericValues = row.values.filter(
(value): value is CellData => !!value && typeof value.effectiveValue === "number"
);
return acc + numericValues.reduce(
(inner, cell) => inner + (cell.effectiveValue ?? 0),
0
);
}, 0);
totals.set(chunk.sheetId, (totals.get(chunk.sheetId) ?? 0) + sum);
// The cellXfs map can be sent elsewhere once you're done streaming
console.log("Formats registry entries", chunk.cellXfs.size);
}Export to Excel
Toolkit provides createExcelFile and exportToExcel functions to either create a excel ArrayBuffer or create a downloadable excel file.
// Export toolkit
import {
createExcelFile,
exportToExcel,
} from "@rowsncolumns/toolkit";
import React, { useState } from "react"
import { useSpreadsheetState, SheeData } from "@rowsncolumns/spreadsheet-state"
import { functionDescriptions, functions } from "@rowsncolumns/functions";
import {
SpreadsheetProvider,
CanvasGrid,
CellData,
Sheet,
EmbeddedChart,
EmbeddedObject,
TableView
} from "@rowsncolumns/spreadsheet"
const MySpreadSheet = () => {
const [sheets, onChangeSheets] = useState<Sheet[]>([]);
const [sheetData, onChangeSheetData] = useState<SheetData<CellData>>({});
const [scale, onChangeScale] = useState(1);
const [charts, onChangeCharts] = useState<EmbeddedChart[]>([]);
const [embeds, onChangeEmbeds] = useState<EmbeddedObject[]>([]);
const [tables, onChangeTables] = useState<TableView[]>([]);
return (
<button onClick={async () => {
// Download excel file
await exportToExcel({
filename: "my_excel_file",
sheets,
sheetData,
tables
})
// OR Create a Array buffer
const blob = await createExcelFile({ sheets, sheetData, tables })
}}>Download excel</button>
)
}Last updated
Was this helpful?