How to export an HTML table to Excel with JavaScript

Two ways to let visitors download a table from your page: a small CSV function with no library, or a real .xlsx file with SheetJS.

1. CSV, no library

function downloadTableAsCsv(table, filename = 'table.csv') {
  const quote = text => {
    text = text.replace(/\s+/g, ' ').trim();
    if (/^[=+\-@]/.test(text)) text = "'" + text; // stop Excel from running cell text as a formula
    return /[",\n]/.test(text) ? '"' + text.replace(/"/g, '""') + '"' : text;
  };
  const rows = [...table.rows].map(row => [...row.cells].map(cell => quote(cell.innerText)).join(','));
  const blob = new Blob(['' + rows.join('\r\n')], { type: 'text/csv;charset=utf-8' }); // BOM: Excel reads UTF-8
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = filename;
  a.click();
  URL.revokeObjectURL(a.href);
}

Call it from a button: downloadTableAsCsv(document.querySelector('#my-table'), 'report.csv'). What each part does, checked on our test table:

The catch: a CSV has no types, so the spreadsheet guesses. LibreOffice turned the ID 0042 into 42 and 1,234.50 into the number 1234.5 (in an English locale). If that matters, use .xlsx.

2. A real .xlsx with SheetJS

<script src="https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js"></script>
<script>
function downloadTableAsXlsx(table, filename = 'table.xlsx') {
  const workbook = XLSX.utils.table_to_book(table, { raw: true });
  XLSX.writeFile(workbook, filename);
}
</script>

SheetJS reads the table and writes an Excel workbook in the browser, with no server involved. Our results:

Cell on the pageDefaultWith { raw: true }
0042number 42text "0042"
1,234.50number 1234.5text "1,234.50"
=1+1text (not run as a formula)text
two lines with <br>one cell, line break keptsame

Use the default when the table holds real numbers you want to add up, and raw: true when it holds IDs, phone numbers or codes. The library file is about 950 KB, so load it only on pages that need it. Pin the version you tested, and get it from SheetJS's own CDN (cdn.sheetjs.com): the xlsx package on the npm registry is an old version.

Which one?

CSV for small tables and minimal code; .xlsx when the data has leading zeros, long numbers or line breaks, or when users expect a "real" Excel file. To convert a table once, without writing code, paste it into the free HTML table to CSV converter.

Frequently asked questions

Why does Excel show strange characters in my CSV?

The file is UTF-8 without a BOM, so the spreadsheet guesses another encoding. Start the file with \uFEFF, as in the code above. With it, accented and Chinese text opened correctly in our test.

How do I keep leading zeros when exporting to Excel?

CSV can't: the spreadsheet decides the type when it opens the file (0042 became 42 in our test). Export .xlsx with SheetJS and { raw: true }, which stores the cell as text.

Can I export a table with formatting (colors, bold)?

Not with these methods: both export values only. According to its documentation, the free SheetJS edition doesn't write cell styles.

Does this send the data to a server?

No. Both methods build the file in the browser and download it directly.

Written with AI assistance. Both methods were run in Chromium 153 on 2026-10-01 on a test table with accents, Chinese text, quotes, commas, a leading-zero ID, a formula-like cell and a <br>; the files were opened in LibreOffice Calc. We did not test Microsoft Excel itself. PlainTables is our extension; the code on this page is free to use, and SheetJS is a free open-source library.