Skip to main content
Back to BlogConverter Guides

How to Convert CSV to XLSX (and Why a Real Spreadsheet Is a ZIP of XML)

Convert CSV to a real XLSX spreadsheet — and learn why .xlsx is a ZIP archive of XML files not a single format, why numbers must be stored as numbers not strings for SUM to work, why the delimiter is auto-detected not assumed, and why the shared strings table keeps the file small.

The Toolbox TeamAugust 14, 20268 min read

The problem: CSV is text, XLSX is a ZIP of XML, and they have nothing in common

You have a CSV file — 50 columns, 10,000 rows, comma-separated, numbers and dates and text all as plain strings. You need it as an Excel spreadsheet, and the lazy export tools do one of two wrong things: they rename the .csv to .xls (Excel opens it, complains about the format mismatch, and every cell is a string so SUM returns 0), or they generate an HTML table with an .xls extension (same complaint, same broken formulas). The right way is to build a genuine Office Open XML (.xlsx) file — a ZIP archive containing XML parts that Excel reads natively, with numbers stored as numbers, dates as dates, and strings in a shared string table. The tool does this in the browser, and the difference between this and the rename hack is the difference between a spreadsheet that works and one that doesn't.

Fastest path

Open the CSV to Excel Converter, paste the CSV or upload the .csv file, click Download .xlsx.

Input (CSV):
  Name,Email,Age,Salary,Start Date
  Alice,alice@example.com,29,75000,2023-01-15
  Bob,bob@example.com,34,82000,2021-06-01

Output (.xlsx):
  → 5 columns, 2 data rows
  → Age and Salary: numeric cells (SUM works)
  → Start Date: date cells
  → Name, Email: shared strings
  → Bold header row

The tool parsed the CSV, detected the delimiter (comma), typed each column (number, date, string), built the XLSX as a ZIP of XML parts via JSZip, and downloaded it. Open it in Excel, Google Sheets, or LibreOffice — no format warning, formulas work. The rest of this guide is what's inside the ZIP, why type detection matters, and why the delimiter is auto-detected instead of assumed.

The substance: one ZIP, six XML parts, one type system

The ZIP and its six parts

An .xlsx file is a ZIP archive containing six XML parts. If you rename a .xlsx to .zip and extract it, you see them:

Part What it does
[Content_Types].xml Tells the consuming app "this is a spreadsheet"
_rels/.rels The root relationship — points to the workbook
xl/workbook.xml Lists the sheets (Sheet1, Sheet2…)
xl/worksheets/sheet1.xml The actual cell data — rows, columns, values
xl/styles.xml Fonts, fills, borders (bold headers live here)
xl/sharedStrings.xml Deduplicated string table — every text cell references an index here

The tool builds all six with JSZip, then calls generateAsync() to produce the .xlsx blob. The browser downloads it as a real spreadsheet. No HTML-table hack, no format mismatch warning.

The shared strings table: why .xlsx is smaller than .csv

Excel stores text cells as integers, not strings. If the value "Alice" appears in 500 cells, the string "Alice" is stored once in sharedStrings.xml and each of the 500 cells holds <c t="s"><v>42</v></c> — a reference to index 42 in the shared string table. The string data appears once; the cells carry a 2-byte integer.

This is why XLSX files are often smaller than the CSV they came from, despite the XML overhead. A CSV with 10,000 rows of "Active" in a status column writes "Active" 10,000 times (70,000 characters). The XLSX writes it once in sharedStrings.xml and 10,000 cells carry the index. The tool's getSSIndex() function does this deduplication — it builds a Map<string, number> and returns the existing index for repeated values.

Type detection: why SUM needs numbers

The critical decision is whether to store a cell as a number or a string. 75000 stored as a number is <c r="B2"><v>75000</v></c> — Excel knows it's a number, SUM(B2:B100) works, you can format it as currency. The same 75000 stored as a string is <c r="B2" t="s"><v>5</v></c> — Excel treats it as text, SUM returns 0, and you see a green triangle in the cell warning "number stored as text."

The tool's detectCellType() runs three checks on each value:

  1. Number: Number(trimmed) returns a finite value → numeric cell. 75000, 3.14, -42 all pass.
  2. Date: matches one of three patterns (YYYY-MM-DD, MM/DD/YYYY, MM-DD-YYYY) and new Date(trimmed) is valid → date cell.
  3. String: everything else → shared string.

The auto-detect toggle lets you turn this off — if your data has postal codes like 02134 that should stay as text (leading zero), turn it off or Excel will store 02134 as 2134 and lose the zero. The tool's preview table marks detected types with # for numbers and D for dates, so you can verify before downloading.

Delimiter detection: comma is not the only separator

CSV stands for "comma-separated values," but European CSVs use semicolons (because their locale uses commas as decimal separators: 1,5 for one-and-a-half). TSV files use tabs. Pipe-delimited files use |. The tool auto-detects the delimiter by scoring four candidates (,, ;, \t, |) on the first 5 lines:

Score = averageDelimiterCount − variance

The delimiter with the highest score wins. The − variance term is the key: a delimiter that appears a consistent number of times per line (low variance) is the real separator. A character that appears sometimes but not consistently (high variance) is probably part of the data, not the delimiter. The tool handles quoted fields correctly — a comma inside "Smith, John" is not counted as a delimiter because the parser tracks the quote state.

The quoted-field state machine

CSV parsing is not text.split(','). A field can contain the delimiter if it's wrapped in double quotes: "Smith, John",29,Alice. A field can contain a literal double quote by escaping it as two quotes: "He said ""hello""". A field can contain a newline: "Line 1\nLine 2". The tool's parser is a character-by-character state machine with two states (inside quotes, outside quotes) that handles all three cases. A naive split(',') breaks on all of them — it splits "Smith, John" into two fields, drops the escaped quotes, and truncates the multiline field at the first newline.

Gotchas

  • Numbers stored as text break formulas. If SUM returns 0, the cells are strings, not numbers. The tool's auto-detect stores 75000 as a number; if you turned it off, every value is a string and formulas won't work. Check the preview for # markers on numeric columns.
  • Postal codes and phone numbers lose leading zeros. 02134 stored as a number becomes 2134. Turn off auto-detect for columns that should stay as text, or prefix them with a non-numeric character before converting.
  • European CSVs use semicolons, not commas. The tool auto-detects this, but if your data has both commas and semicolons (rare but possible), the detector picks the one with the lower variance. Check the detected delimiter in the options panel before downloading.
  • Date detection is limited to three formats. YYYY-MM-DD, MM/DD/YYYY, and MM-DD-YYYY are detected. DD/MM/YYYY (UK format) looks like MM/DD/YYYY to the detector and may be parsed as the wrong date. If your dates are ambiguous, turn off auto-detect and handle dates in Excel after conversion.
  • The XLSX has no formulas. The tool writes static values, not Excel formulas. If your CSV contains formula text like =SUM(A1:A10), it's stored as a string, not an executable formula. Excel won't evaluate it. Add formulas in Excel after conversion.
  • One sheet only. The tool creates a single sheet named "Sheet1." If your CSV contains multiple sections separated by blank lines, they all go into one sheet. Split the CSV first, or convert each section separately.
  • Large CSVs take time in the browser. The tool parses and generates the XLSX client-side. A 100,000-row CSV takes a few seconds; a million-row CSV may freeze the tab. The tool is designed for datasets under ~500K rows.
  • Renaming .csv to .xls is not conversion. Excel opens it with a format warning, every cell is a string, and formulas don't work. The tool generates a real .xlsx — no warning, typed cells, working formulas.

Summary

  • .xlsx is a ZIP of six XML parts, not a single file. The tool builds all six via JSZip: content types, relationships, workbook, worksheet, styles, and shared strings. Rename a .xlsx to .zip and extract it to see them.
  • The shared strings table deduplicates text. "Alice" in 500 cells is stored once; each cell carries an integer index. This is why XLSX is often smaller than CSV despite XML overhead.
  • Type detection decides whether SUM works. Numbers stored as numbers → formulas work. Numbers stored as strings → SUM returns 0, green triangles everywhere. The tool detects numbers, dates, and strings; toggle it off for postal codes and phone numbers that need leading zeros.
  • Delimiter detection scores four candidates (,, ;, \t, |) by average count minus variance on the first 5 lines. European CSVs use ; because their locale uses , as the decimal separator. The parser handles quoted fields, escaped quotes, and newlines inside fields — a naive split(',') breaks on all three.
  • All processing is in the browser. No CSV data is uploaded to any server. The parsing, typing, and XLSX generation all happen client-side.
  • Convert at the CSV to Excel Converter; for the reverse use Excel to CSV Converter, for JSON output use CSV to JSON Converter, and for JSON input use JSON to CSV Converter.