XLSX Import & Export
ReoGrid Web can read and write Excel format (.xlsx) files. Load spreadsheets from a URL, a File object, or raw binary data, and export back to xlsx for download.
Import
Load a file on the grid instance. Every sheet in the workbook comes in, one worksheet per sheet, with the sheet tab bar to move between them, and the sheet that was active in Excel opens first.
Loading from a URL
await grid.loadFromUrl('/data/sample.xlsx')
Loading from a File Object
Load a File obtained from a file input element or drag-and-drop.
// File input
const input = document.querySelector<HTMLInputElement>('#file-input')
input.addEventListener('change', async () => {
const file = input.files?.[0]
if (file) await grid.loadFromFile(file)
})
// Drag & drop
container.addEventListener('drop', async (e) => {
e.preventDefault()
const file = e.dataTransfer?.files[0]
if (file?.name.endsWith('.xlsx')) {
await grid.loadFromFile(file)
}
})
Loading from ArrayBuffer / Uint8Array
const response = await fetch('/data/sample.xlsx')
const buffer = await response.arrayBuffer()
await grid.loadXlsx(buffer)
A load replaces the worksheets, so read grid.worksheet again after it rather than holding on to a worksheet from before.
Loading a single sheet
To pull one sheet of a file into one existing worksheet — and leave the rest of the workbook alone — use the worksheet’s single-sheet loaders:
const ws = grid.worksheet
await ws.loadSheetFromUrl('/data/sample.xlsx', { sheetName: 'Q2 Forecast' })
await ws.loadSheetFromFile(file, { sheetName: 'Q2 Forecast' })
await ws.loadSheetFromBuffer(buffer, { sheetName: 'Q2 Forecast' })
sheetName picks the sheet; without it the first sheet is used. No tabs are added and the file’s other sheets are not read.
Renamed in v1.7.0
Before v1.7.0 these single-sheet loaders had exactly the same names as the workbook loaders on the instance. So grid.worksheet.loadFromFile(file) read like “open this file” while quietly keeping only the first sheet — and a workbook whose first tab is a cover or index page came up looking blank. They now say what they do:
| Old name on the worksheet (deprecated) | To open the whole workbook | To load one sheet |
|---|---|---|
worksheet.loadFromFile(file) | grid.loadFromFile(file) | worksheet.loadSheetFromFile(file) |
worksheet.loadFromUrl(url) | grid.loadFromUrl(url) | worksheet.loadSheetFromUrl(url) |
worksheet.loadXlsx(data) / worksheet.loadFromBuffer(data) | grid.loadXlsx(data) | worksheet.loadSheetFromBuffer(data) |
The old names still work and behave exactly as before, but they are marked @deprecated (editors strike them through), print a one-time console warning naming both replacements, and will be removed in 2.0. If your code calls one of them, it almost certainly meant the whole workbook — switch to the instance method.
Load Options
await grid.loadFromUrl('/data/sample.xlsx', {
chunked: true, // Chunked async ingest (snappier first paint on big files)
})
await grid.worksheet.loadSheetFromUrl('/data/sample.xlsx', {
sheetName: 'Sheet2', // Which sheet to load (defaults to the first sheet)
})
| Option | Type | Default | Description |
|---|---|---|---|
sheetName | string | first sheet | Single-sheet loaders only: name of the sheet to load. The workbook loaders always load every sheet. |
chunked | boolean | { batchSize } | false | Chunked async ingest. true uses the default batch size; pass { batchSize } to control it. First paint lands in ~40 ms instead of one multi-second freeze on large files. |
Images embedded in the xlsx are always loaded — there is no option to skip them.
How an imported file looks
Default font
Cells the file stores without a style of their own open in the workbook’s default font — its Normal style — so a typical Excel file shows its plain cells in Calibri 11, or 游ゴシック 11 / MS Pゴシック 11 in a Japanese workbook. Before v1.7.0 they fell back to ReoGrid’s own Arial 10, so the untouched cells of a table came out in a different font and size from the formatted cells beside them. Cells that are not in the file at all still start in ReoGrid’s default when you type into them.
Formulas ReoGrid cannot calculate
Some formulas the engine cannot evaluate — a reference to another workbook (=[1]Sheet1!A1*2), a structured table reference (=SUM(Table1[Qty])), or other syntax it does not parse. Since v1.7.0 such a cell shows the result Excel saved in the file, rather than the formula text:
- other formulas can use that value, and it moves with the cell on insert/delete, sort and move;
- the formula itself is kept and written back on export, so Excel recalculates it as before;
- it is not recalculated in ReoGrid, and editing the cell replaces it.
Tables with Excel’s “None” style
A table that Excel shows with no style imports plain, and is written back without a style name — before v1.7.0 it came up with TableStyleMedium2’s blue header and banded rows. See Table Styles.
Zoom and hyperlinks
The sheet’s zoom and its hyperlinks are read as well (v1.7.0), so a workbook saved at 85% opens at 85% and its links open on click.
Export
Download as xlsx
Note: xlsx export (
saveAsXlsx) is available in the Pro edition.
const ws = grid.worksheet
// Download with default filename
ws.saveAsXlsx()
// Specify filename and sheet name
ws.saveAsXlsx({
filename: 'report.xlsx',
sheetName: 'Sheet1',
})
| Option | Type | Default | Description |
|---|---|---|---|
filename | string | 'reogrid.xlsx' | Download filename |
sheetName | string | 'Sheet1' | Sheet name within the xlsx file |
worksheet.saveAsXlsx() saves that one sheet. To save every sheet into one file, call it on the instance: grid.saveAsXlsx({ filename: 'report.xlsx' }).
What Excel sees
Since v1.7.0 an exported workbook opens in Excel looking like the grid. Earlier versions differed in several ways, all fixed in the writer:
| Before v1.7.0 | Now | |
|---|---|---|
| Row heights | Written in pixels where the format expects points, so every row came out a third taller in Excel (and taller again on each re-import) | Written in points (px × 0.75); a round trip gives back the same heights |
| Number formats | Not written — every cell was General | Written (#,##0, [$¥]#,##0, dates, percentages …), including on empty input cells that carry a format |
| Numbers typed into cells | "135000" written as text, so Excel neither calculated with it nor applied its format | Plain decimals are written as numbers; text that only looks numeric to JavaScript ("007", "1e3") stays text |
| Line breaks | A value containing \n showed on one line | Wrap text is turned on for it, as Excel does for Alt+Enter |
| Empty styled cells | Written as empty strings, which Excel counts as filled — a label could not spill across them and was cut off | Written without a value |
Export Snapshot
If you want to work with xlsx data programmatically, you can obtain a snapshot.
const snapshot = ws.getExportSnapshot()
// snapshot contains all cell data, styles, merges, column widths, etc.
Supported Content
| Data | Supported |
|---|---|
| Cell values (text, numbers) | Yes |
| Formulas | Yes |
| Cell styles (font, color, alignment) | Yes |
| Number formats | Yes (export since v1.7.0) |
| Cell merges | Yes |
| Column widths / row heights | Yes |
| Borders | Yes |
| Row / column outlines (level, hidden, collapsed, summary direction) | Yes |
| Conditional formatting (incl. per-side borders) | Yes |
| Theme colors and tint | Yes (import) |
| Images | Yes — import and export (v1.6.0; real xl/media + xl/drawings) |
| Cell comments (memo) | Yes — import and export (v1.6.0; text, author, pinned state) |
| Print area | Yes — import and export (v1.6.0; _xlnm.Print_Area) |
| Page setup, margins, header/footer, page breaks | Yes |
| Fit to pages | Yes (<pageSetUpPr fitToPage>) |
| Text rotation / vertical text | Yes (<alignment textRotation>) |
| Hyperlinks | Yes — import and export (v1.7.0; see Hyperlinks) |
| Zoom | Yes — import and export (v1.7.0; zoomScale, see Zoom) |
| Tables with no style (Excel’s “None”) | Yes (v1.7.0) |
Three things that used to be silently dropped on save now round-trip, as of v1.6.0: floating images, cell comments, and the print area. If you previously worked around any of them by persisting to ReoGrid JSON instead, you no longer need to.
ReoGrid JSON Format
See the dedicated ReoGrid JSON Format page for the full API, document anatomy, multi-sheet I/O, and the xlsx → JSON conversion pattern.
For application state persistence — saving and restoring full worksheet state without going through xlsx — use the ReoGrid JSON format. It is lossless and covers everything the runtime needs: cells, styles, number formats, rich text, merges, borders, sizes/visibility, freeze, conditional formats, outlines, filter, cell types, protection, and alternate rows.
import {
writeReoGridJson,
readReoGridJson,
parseReoGridJson,
type ReoGridJsonDocument,
} from '@reogrid/lite'
// Serialize
const doc: ReoGridJsonDocument = writeReoGridJson(worksheet)
const json = JSON.stringify(doc)
localStorage.setItem('my-sheet', json)
// Restore
const saved = localStorage.getItem('my-sheet')
if (saved) {
const parsed = parseReoGridJson(saved) // JSON string -> document
readReoGridJson(worksheet, parsed) // apply to worksheet
}
Both @reogrid/lite and @reogrid/pro re-export these from their main entry — no deep imports required.
Headless import
The browser loaders — grid.loadFromUrl / loadFromFile / loadXlsx and the worksheet’s loadSheetFrom* — need the DOM. For a build tool that turns customer .xlsx files into templates outside a browser, the importer is exported directly:
import { XlsxImporter, convertImageInfo } from '@reogrid/pro'
const importer = new XlsxImporter(worksheet)
const result = importer.load(bytes) // or: await importer.loadAsync(bytes)
// Images come back in the result — hand each to the worksheet explicitly
for (const image of result.images) {
worksheet.loadImageFromImport(convertImageInfo(image))
}
Retrieving Images
Access images loaded from an xlsx file. Since v1.6.0 you can also insert, move and resize them — see Images.
Note:
createImageUrl()/revokeImageUrl()are available in the Pro edition.
// List images
const images = ws.getImages()
// Generate Blob URLs
images.forEach((img) => {
const url = ws.createImageUrl(img.id)
console.log(img.id, url)
})
// Release Blob URLs when no longer needed
ws.revokeImageUrl(imageId)
// Image change event
ws.onImagesChange((images) => {
console.log('Images:', images)
})
Example: File Operations in React
import { useRef } from 'react'
import { Reogrid } from '@reogrid/pro/react'
import type { ReogridInstance } from '@reogrid/pro/react'
function SpreadsheetApp() {
const gridRef = useRef<ReogridInstance>(null)
async function handleFileChange(e: React.ChangeEvent<HTMLInputElement>) {
const file = e.target.files?.[0]
if (file) {
await gridRef.current?.loadFromFile(file)
}
}
function handleExport() {
gridRef.current?.worksheet.saveAsXlsx({ filename: 'export.xlsx' })
}
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100vh' }}>
<div>
<input type="file" accept=".xlsx" onChange={handleFileChange} />
<button onClick={handleExport}>Export</button>
</div>
<Reogrid ref={gridRef} style={{ flex: 1 }} />
</div>
)
} Sorry to hear that. What could be improved?