๐จ Tutorial ยท 7 min read
How to Read a CSV File in JavaScript
In the browser, read the file with await file.text() (or a FileReader), then parse the string with a quote-aware parser. In Node.js, read it with fs.readFileSync('data.csv', 'utf8') or stream it into csv-parse. The part that trips people up is not the file access, it is the parsing: any code that calls text.split(',') or line.split(',') is wrong on real data, because a comma is allowed inside a quoted field. This guide covers both environments, the one file that breaks a naive split, and which parser to reach for.
The quick answer
// Browser: a File from <input type="file"> or a drop event
const text = await file.text();
// Node: read the whole file into a string
import { readFileSync } from "node:fs";
const text = readFileSync("data.csv", "utf8");
// Then parse with a quote-aware parser โ not split(",")
Get the text first, then hand it to a parser. Everything below is which parser, and why the shortcut is a bug rather than a style choice.
Browser: FileReader, File.text() and Blob.text()
When the user picks a file or drops one on the page, what you have is a File, which extends Blob. Modern browsers give you a promise-returning method on it, so the shortest correct read is:
const file = input.files[0];
const text = await file.text(); // Blob.text(), resolves to a UTF-8 string
Older code โ and any code that needs progress on a large file โ uses FileReader instead:
const reader = new FileReader();
reader.onload = () => console.log(reader.result);
reader.readAsText(file, "utf-8");
Both produce a string. If the file is not UTF-8, readAsText lets you pass an encoding (for example "windows-1252"); file.text() always assumes UTF-8, so a legacy export read that way shows รยฉ where an accent should be. That symptom is the same one described in fixing a garbled CSV, with an encoding label instead of a file setting.
What the browser will not do for you is parse. Read the string, then parse it.
Node.js: fs, and streaming for large files
On the server, the file is on disk and you read it with the built-in fs module. For a file you know fits in memory:
import { readFileSync } from "node:fs";
const text = readFileSync("data.csv", "utf8"); // or await fs.promises.readFile(...)
For a file that does not fit โ a few hundred megabytes, or a nightly export โ stream it. csv-parse is written as a stream transform, so it reads record by record and never holds the whole file:
import { createReadStream } from "node:fs";
import { parse } from "csv-parse";
createReadStream("data.csv")
.pipe(parse({ columns: true, bom: true }))
.on("data", (row) => console.log(row));
The columns: true option turns the first row into object keys, so each row is { name: ..., city: ... } rather than an array. That is usually what you want. The same streaming idea in the shell is covered in opening a CSV in Linux, and the Python equivalent in opening a CSV in Python.
Why split(",") is wrong on real data
Here is the rule that catches everyone. RFC 4180, the CSV format definition, allows a field to contain the delimiter โ and even a line break โ as long as the field is enclosed in double quotes. A single row from a CRM export can look like this:
name,city,note
"Doe, Jane",Paris,"likes ""quotes""
and commas"
Bob,Tokyo,plain
Three records. But split on the comma and you get five fragments, and split on newlines and you get four lines. Every tool that assumes one row is one line and one field is one comma-separated piece reads this file incorrectly:
text.split("\n").map(line => line.split(","))
// [["name","city","note"],
// ["\"Doe"," Jane\"","Paris","\"likes \"\"quotes\"\""],
// ["and commas\""],
// ["Bob","Tokyo","plain"]] โ wrong: 4 rows, ragged columns
A quote-aware parser reads the same text correctly, as three records with three fields each. The rule to carry: the moment you split a CSV on a single character, you are asserting that no field contains that character. Exports from forms, banks and CRMs break that assertion routinely.
Which parser: Papa Parse, csv-parse, or d3-dsv
Three libraries cover almost every JavaScript need. All are quote-aware.
- Papa Parse โ one dependency that runs in the browser and in Node. It accepts a
Fileor a string, has a worker mode for large uploads, and can stream a remote URL. Best when the file arrives from a user in the browser. - csv-parse โ part of the
csvpackage, Node-first, built as a stream. Best for files on disk and for pipelines. - d3-dsv โ small and dependency-light, with
d3.csvParsefor a string. A good fit when you are already working with data visualisation and only need a plain parse.
// Papa Parse (browser or Node)
Papa.parse(file, { header: true, skipEmptyLines: true,
complete: (res) => console.log(res.data) });
// d3-dsv (string in, array of objects out)
import { csvParse } from "d3-dsv";
const rows = csvParse(text);
Any of these beats a hand-rolled parser, but if you only need to look at the data rather than ship code, the free CSV analyzer reads it in the browser with nothing to install. To turn the records into JSON, converting CSV to JSON covers the mapping, and the JSON โ CSV converter does it interactively.
Headers, the BOM, and empty trailing lines
Three small details cause most "it parsed but the fields are wrong" reports.
- A UTF-8 BOM. If the file was saved as "UTF-8 with BOM", the first header key becomes
"\uFEFFname"androw.nameisundefined. Strip it withtext.replace(/^\uFEFF/, ""), or let a parser do it (bom: truein csv-parse, on by default in Papa Parse). - A trailing newline. Most editors end the last line with a newline, which yields one empty final row. Use
skipEmptyLines: true, or filter rows with no populated fields. - Type inference. Parsers return strings. A zip code of
01234stays a string only if you keep it that way โ converting it to a number drops the leading zero, the same trap covered in keeping leading zeros.
Counting and checking the parse
After parsing, the fastest sanity check is the record count, because a quoted line break is the one thing that silently changes it. If the count disagrees with your expectation, compare it against the physical line count โ the mismatch is almost always a multi-line field. Counting rows in a CSV walks through that comparison.
const records = Papa.parse(text, { header: true }).data;
console.log(records.length); // records, not lines
Frequently Asked Questions
How do I read a CSV file in JavaScript?
In the browser, read the File with await file.text(), then parse the string with Papa Parse. In Node, use fs.readFileSync('data.csv', 'utf8'), or stream with createReadStream piped into csv-parse. Do not split on the comma.
Why does splitting on a comma give the wrong column count?
RFC 4180 allows a comma, and a line break, inside a double-quoted field. Splitting sees every comma, so "Doe, Jane",Paris becomes three pieces instead of two. A quote-aware parser reads the field as one value.
Should I use Papa Parse or csv-parse?
Papa Parse runs in both the browser and Node and reads a File directly, which suits user uploads. csv-parse is a Node stream transform, which suits large files and pipelines. Both handle quotes correctly.
How do I handle a BOM at the start of a CSV?
A UTF-8 BOM becomes a literal U+FEFF at the front of the string, so the first header key reads as "\uFEFFname". Strip it with text.replace(/^\uFEFF/, ""), or rely on a parser that removes it by default.
NoCodeCSV Team
Updated October 9, 2026 ยท Practical guides by the NoCodeCSV team.
Go further with AI data tools
NoCodeCSV handles the basics for free. When your data work grows, these tools pair well with it:
- Stack AI โ build AI workflows that process your CSVs automatically, end to end. Try Stack AI
- Softr โ turn your cleaned data into customer-facing apps and portals without code. Try Softr
- Toggl Track โ track time spent on data projects and client work. Try Toggl
Some links above are affiliate links โ if you buy through them we may earn a commission at no extra cost to you.
Inspect a CSV Without Writing Code
Upload any CSV and read it as a clean table in seconds. No signup needed.
Related reading
CSV Fundamentals โ other guides that pair well with this one.
- Why a CSV Can Be Bigger Than XLSX
- How to Open a CSV File
- AI CSV Editor: Edit a CSV with Plain English
- CSV vs JSON
Browse all guides in the NoCodeCSV blog.