$ snippet parse-csv-browser
Parse CSV in the browser
A correct CSV parser in 25 lines: quoted fields, embedded commas and newlines, doubled quotes — plus a File input example.
/** Parse CSV text into an array of rows (arrays of strings). RFC 4180 compliant. */
function parseCSV(text, delimiter = ",") {
const rows = [];
let row = [], field = "", inQuotes = false;
for (let i = 0; i < text.length; i++) {
const c = text[i];
if (inQuotes) {
if (c === '"') {
if (text[i + 1] === '"') { field += '"'; i++; } // escaped quote
else inQuotes = false;
} else field += c;
} else if (c === '"' && field === "") inQuotes = true;
else if (c === delimiter) { row.push(field); field = ""; }
else if (c === "\n" || c === "\r") {
if (c === "\r" && text[i + 1] === "\n") i++;
row.push(field); rows.push(row); row = []; field = "";
} else field += c;
}
if (field !== "" || row.length) { row.push(field); rows.push(row); }
return rows;
}
/** First row as headers → array of objects. */
function csvToObjects(text, delimiter = ",") {
const [header, ...rows] = parseCSV(text, delimiter);
return rows.filter(r => r.length > 1 || r[0] !== "")
.map(r => Object.fromEntries(header.map((h, i) => [h.trim(), r[i] ?? ""])));
}
/** Usage with a file input: <input type="file" accept=".csv"> */
async function readCSVFile(file) {
return csvToObjects(await file.text());
}How it works
text.split(",") fails the moment a field contains a comma inside quotes, and split("\n") fails on quoted newlines — both common in real exports from Excel and CRMs. A character-by-character state machine handles the three rules of CSV: fields may be quoted, quoted fields may contain delimiters and line breaks, and a literal quote is written as "".
Gotchas
- Excel often saves CSV with a UTF-8 BOM (
\uFEFF) as the first character; strip it or your first header will be"\uFEFFid". - Some locales export with
;as the delimiter — sniff the first line if you accept uploads from anywhere. - For files over ~50 MB, parse in chunks with a stream instead of
file.text(); this parser holds everything in memory.
Ad slot