ReoGrid ReoGrid Web

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.

Live Demo

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 workbookTo 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)
})
OptionTypeDefaultDescription
sheetNamestringfirst sheetSingle-sheet loaders only: name of the sheet to load. The workbook loaders always load every sheet.
chunkedboolean | { batchSize }falseChunked 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.

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',
})
OptionTypeDefaultDescription
filenamestring'reogrid.xlsx'Download filename
sheetNamestring'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.0Now
Row heightsWritten 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 formatsNot written — every cell was GeneralWritten (#,##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 formatPlain decimals are written as numbers; text that only looks numeric to JavaScript ("007", "1e3") stays text
Line breaksA value containing \n showed on one lineWrap text is turned on for it, as Excel does for Alt+Enter
Empty styled cellsWritten as empty strings, which Excel counts as filled — a label could not spill across them and was cut offWritten 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

DataSupported
Cell values (text, numbers)Yes
FormulasYes
Cell styles (font, color, alignment)Yes
Number formatsYes (export since v1.7.0)
Cell mergesYes
Column widths / row heightsYes
BordersYes
Row / column outlines (level, hidden, collapsed, summary direction)Yes
Conditional formatting (incl. per-side borders)Yes
Theme colors and tintYes (import)
ImagesYes — 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 areaYes — import and export (v1.6.0; _xlnm.Print_Area)
Page setup, margins, header/footer, page breaksYes
Fit to pagesYes (<pageSetUpPr fitToPage>)
Text rotation / vertical textYes (<alignment textRotation>)
HyperlinksYes — import and export (v1.7.0; see Hyperlinks)
ZoomYes — 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>
  )
}
Was this page helpful?
Stay Updated

Be first to know — get updates as they ship

Get notified of new releases, features, and announcements.
No spam — just updates that matter.