# Spreadsheet

> Read CSV and Excel files in the browser, match their columns to your fields, and write CSV that Excel opens with diacritics intact.

- Type: React helper (Hooks & Utilities)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add spreadsheet`
- Docs: https://wingo-ui.com/components/spreadsheet

## Interaction

Choose a CSV or an .xlsx file (or keep the sample, a ';' export from a European Excel): the demo shows the detected delimiter and encoding, the header row, how each column matched a Northwind CRM field (Company name → Company, VAT ID → Tax ID) with its score, and the first rows. Download CSV writes the parsed rows back with the delimiter from the playground, a byte order mark and formula cells neutralised.

## Installation

```bash
npx wingo-ui@latest add spreadsheet
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/spreadsheet` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `lib/spreadsheet.ts`

npm dependencies: `npm i read-excel-file`

Wingo UI dependencies (installed automatically):

- [Text match](https://wingo-ui.com/components/text-match) ([Markdown](https://wingo-ui.com/components/text-match.md))

## Usage

```tsx
import { matchColumns, readSpreadsheet, SpreadsheetError, toCsv } from "@/lib/spreadsheet"

async function onFile(file: File) {
  try {
    const { sheets } = await readSpreadsheet(file)
    const [sheet] = sheets
    const mapping = matchColumns(sheet.headers, [
      { id: "name", label: "Company", aliases: ["Company name", "Customer"] },
      { id: "taxId", label: "Tax ID", aliases: ["VAT ID", "VAT number"] },
    ])
    return { sheet, mapping }
  } catch (error) {
    if (error instanceof SpreadsheetError && error.code === "unsupported") return null
    throw error
  }
}

toCsv([["Customer", "Balance"], ["Northwind Tech LLC", 12480]], { delimiter: ";" })
```

## API

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `delimiter?` | `string` | `","` | toCsv options.delimiter for the download: "," or ";" (what a Romanian Excel expects, because "," is its decimal mark). |
| `escapeFormulas?` | `boolean` | `true` | toCsv options.escapeFormulas: cells starting with = + - @ get a leading ' so a spreadsheet never runs them (CSV injection). |
| `threshold?` | `number` | `0.5` | matchColumns options.threshold: the lowest score that still maps a column to a field. |
| `readSpreadsheet?` | `(file: Blob & { name?: string }, options?: { maxRows?; maxBytes?; header?: "auto" \| number \| false; delimiter?; columnLabel? }) => Promise<SpreadsheetWorkbook>` |  | A picked file as { kind: "csv" \| "xlsx", sheets: { name, headers, rows, truncated }[], delimiter?, encoding? }. CSV / TSV / TXT are decoded as UTF-8, else Windows-1250 (a Romanian Excel), with the delimiter detected; .xlsx loads read-excel-file on demand. Blank and duplicate headers get names, short rows are padded, empty rows dropped. Throws SpreadsheetError with code "unsupported" (.xls, .numbers), "too-large" (default 20 MB), "empty" or "parse". |

## Parts

### parseCsv (function)

RFC 4180 parsing (quotes, doubled quotes, line breaks inside quotes, CRLF, a BOM), the delimiter among , ; tab | that splits the first lines most evenly, and the text of a file's bytes. Signature: (text, { delimiter?, maxRows? }) => { rows: string[][]; delimiter }.

### detectDelimiter (function)

RFC 4180 parsing (quotes, doubled quotes, line breaks inside quotes, CRLF, a BOM), the delimiter among , ; tab | that splits the first lines most evenly, and the text of a file's bytes. Signature: (text) => string.

### decodeText (function)

RFC 4180 parsing (quotes, doubled quotes, line breaks inside quotes, CRLF, a BOM), the delimiter among , ; tab | that splits the first lines most evenly, and the text of a file's bytes. Signature: (buffer: ArrayBuffer) => { text; encoding }.

### toSheet (function)

Raw rows to a sheet with a header row, and a cell as text for previews and validators (dates as "2026-09-24"). Signature: (name, rows, options?) => SpreadsheetSheet.

### cellToString (function)

Raw rows to a sheet with a header row, and a cell as text for previews and validators (dates as "2026-09-24"). Signature: (cell: SpreadsheetCell) => string.

### matchColumns (function)

Pairs each field with at most one column by name, ignoring case, diacritics and punctuation ("Company name" = "company_name", "Société" = "societe"), aliases included ("VAT ID", "Tax ID"), best scores first, each column used once. The auto-match of an import wizard. Signature: (headers: string[], targets: { id; label; aliases? }[], { threshold? }) => { targetId; column: number | null; score }[].

### toCsv (function)

CSV text with quoting where needed, a UTF-8 BOM by default so Excel keeps accented letters (é, ü, ñ), and a browser download (nothing happens on the server). Signature: (rows, { delimiter?, bom?, lineEnding?, escapeFormulas? }) => string.

### downloadCsv (function)

CSV text with quoting where needed, a UTF-8 BOM by default so Excel keeps accented letters (é, ü, ñ), and a browser download (nothing happens on the server). Signature: (filename, rowsOrText, options?) => void.

### SpreadsheetError (type)

What readSpreadsheet throws, so a UI can print its own message per code. Type: class extends Error { code: "unsupported" | "empty" | "too-large" | "parse" }.

## Examples

- **Export to CSV**: Quotes, accented names and a formula-looking note come out safe for Excel; the button saves orders.csv with a UTF-8 BOM.

## See also

- [File Upload](https://wingo-ui.com/components/file-upload) ([Markdown](https://wingo-ui.com/components/file-upload.md))
- [Text match](https://wingo-ui.com/components/text-match) ([Markdown](https://wingo-ui.com/components/text-match.md))
- [File types](https://wingo-ui.com/components/file-types) ([Markdown](https://wingo-ui.com/components/file-types.md))

---

Canonical: https://wingo-ui.com/components/spreadsheet
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
