javascript csv to array, read csv file into array, and csv to array javascript all describe the same job: turn delimiter-separated text into something you can index in code—usually strings first, then optionally typed fields. javascript parse csv in the browser starts from a string (fetch or FileReader); in Node it starts from fs or a stream.
Tested on: Node.js v20.18.2. A short note after each runnable snippet describes what you should see in the console.
Quick reference
| Situation | Practical approach |
|---|---|
| Single column of values | split('\n'), drop header row |
| Grid of strings | lines.map(line => line.split(',')) if data is simple |
| Rows as objects with headers | First line as keys, map remaining lines |
| Real files (quotes, BOM, Excel) | Papa Parse or d3.csvParse |
| Large files on server | readline, csv-parser, or Papa stream |
Pick a shape for your data
Before you javascript parse csv, pick the shape that matches how the rest of your app will consume rows:
- 1D array — one value per line after a header (lists of ids, names, etc.).
- 2D array — each inner array is one row; every cell stays a string unless you convert later.
- Array of objects — first row becomes keys; each later row becomes
{ header: value }, which reads cleanly in UI code.
The reverse direction, javascript array to csv export, is answered in the page FAQ.
One-dimensional array (single column)
Use this when the file is a single column (plus an optional header): each line becomes one string in the array after you drop the header row.
const csv = `Name
Alice
Bob
Charlie`;
const rows = csv.split('\n');
const oneDimensionalArray = rows.slice(1); // remove the header
console.log(oneDimensionalArray);You should see one line: an array of three strings—Alice, Bob, and Charlie—in that order.
Two-dimensional array (rows × columns)
Use this when every row has the same delimiter and no field contains that delimiter (no quoted commas). You get an array of rows, each row an array of cell strings.
const csv = `Name,Age,Job
Alice,30,Engineer
Bob,40,Doctor`;
const rows = csv.split('\n');
const twoDimensionalArray = rows.map((row) => row.split(','));
console.log(twoDimensionalArray);You should see one line: a nested array whose first row is the header cells, then one row per data line (all values still strings).
Array of objects (header keys)
This pattern maps the first line to property names and each following line to one object. It is easier to consume in components than numeric column indexes (see JavaScript arrays and unique values for follow-on operations).
const csv = `Name,Age,Job
Alice,30,Engineer
Bob,40,Doctor`;
const rows = csv.split('\n');
const headers = rows[0].split(',');
const arrayOfObjects = rows.slice(1).map((row) => {
const values = row.split(',');
const obj = {};
headers.forEach((header, index) => {
obj[header] = values[index];
});
return obj;
});
console.log(arrayOfObjects);You should see one line: two objects whose keys match the header row (Name, Age, Job).
When split(',') is not enough
Naive csv to js array logic breaks when:
- A field contains the delimiter, for example
"Engineer, Senior". - Excel or Windows uses
\r\nline endings (a stray\rcan stick to the last cell). - The file starts with a UTF-8 BOM (
\uFEFF) before the first header name.
For production javascript parse csv workloads, prefer a real parser (below). For a small inline fix, strip the BOM then split lines with a regex that accepts both \n and \r\n:
const raw = '\uFEFFName,Age\nAlice,30';
const text = raw.replace(/^\uFEFF/, '');
const rows = text.split(/\r?\n/).map((line) => line.split(','));
console.log(rows);You should see a two-row grid: headers Name / Age, then Alice / 30, with no BOM character stuck on Name.
Minimal quoted-field parser (single-line rows)
If fields may contain commas but do not contain embedded newlines inside quotes, a small state machine is enough:
const csv = `Name,Age,Job
Alice,30,"Engineer, Senior"
Bob,40,"Doctor"
"Charlie, Jr.",50,Teacher`;
const rows = csv.split('\n');
rows.forEach((row) => {
let insideQuotes = false;
let field = '';
const fields = [];
for (const ch of row) {
if (ch === '"') {
insideQuotes = !insideQuotes;
} else if (ch === ',' && !insideQuotes) {
fields.push(field);
field = '';
} else {
field += ch;
}
}
fields.push(field);
console.log(fields);
});You should see four log lines—one per CSV row—where quoted commas stay inside a single field (for example Engineer, Senior).
Manual parsing from a string (same as fetch / File text)
Whether the text came from fetch, FileReader, or Node fs.readFile, the split approach is the same once you have a string (see also string → array patterns).
const csvString = 'Name,Age,Job\nAlice,30,Engineer\nBob,40,Doctor';
const lines = csvString.split('\n');
const array = lines.map((line) => line.split(','));
console.log(JSON.stringify(array));You should see one JSON string: a header row plus two data rows, every cell still a string.
fetch (browser or Node 18+)
fetch returns a Response; call text() to get the CSV body as a string, then reuse the same row and field splitting as above.
fetch('/data.csv')
.then((response) => {
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.text();
})
.then((data) => {
const lines = data.split(/\r?\n/).filter(Boolean);
return lines.map((line) => line.split(','));
})
.then((array) => console.log(array))
.catch((err) => console.error(err));Use the same parsing as above once response.text() resolves. Cross-origin fetch URLs require CORS to allow the response.
Papa Parse (recommended for real-world CSV)
Papa Parse follows RFC 4180 more closely than naive split, supports header rows, dynamic typing, large files, and Node streams. Use it when user uploads or API responses can contain quoted commas, inconsistent line endings, or malformed rows you want reported in results.errors.
Install:
npm install papaparseOr load in HTML (version pinned here to match npm’s current major line):
<script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.5.3/papaparse.min.js"></script>Parse a CSV string
Papa.parse on a string runs synchronously and returns data plus errors and meta (delimiter, linebreak, fields, and so on).
const Papa = require('papaparse');
const csvString = 'Name,Age,Job\nAlice,30,Engineer';
const result = Papa.parse(csvString, {
header: true,
dynamicTyping: true,
});
console.log(JSON.stringify(result.data));
console.log(JSON.stringify(result.errors));You should see two lines: first, a JSON array with one object (Age is a number because dynamicTyping is on); second, an empty errors array.
Malformed quoted CSV (errors live on results.errors)
Papa Parse does not use an error: callback on the config for parse-time issues; inspect result.errors (and often result.data is empty when parsing aborts early):
const Papa = require('papaparse');
const incorrectCSV = 'Name,Age,"Job\nAlice,30,Engineer';
const result = Papa.parse(incorrectCSV, { header: true, dynamicTyping: true });
console.log(JSON.stringify(result.errors));
console.log(JSON.stringify(result.data));You should see a non-empty errors array describing the broken quote, and data typically empty when parsing cannot complete cleanly.
Browser: parse an uploaded file
<input type="file" id="csvFile" accept=".csv,text/csv" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.5.3/papaparse.min.js"></script>
<script>
document.getElementById('csvFile').addEventListener('change', (event) => {
const file = event.target.files[0];
if (!file) return;
Papa.parse(file, {
header: true,
complete: (results) => {
console.log(results.data);
if (results.errors.length) console.warn(results.errors);
},
});
});
</script>HTML5 File API (FileReader)
Let the user pick a file, read text with FileReader, then reuse the same string parsers as above (naive split or Papa).
document.getElementById('csvFileInput').addEventListener('change', (event) => {
const selectedFile = event.target.files[0];
if (!selectedFile) return;
const reader = new FileReader();
reader.onload = (e) => {
const fileContent = /** @type {string} */ (e.target.result);
const rows = fileContent.split(/\r?\n/).filter(Boolean);
const array = rows.map((row) => row.split(','));
console.log(array);
};
reader.onerror = () => console.error(reader.error);
reader.readAsText(selectedFile);
});For the same sample bytes as the manual string example, the console output matches the manual parsing JSON shown earlier.
See also FileReader on MDN.
Node.js: readline + fs (line by line)
readline with fs.createReadStream walks the file one line at a time, so memory stays bounded. Pair it with split(',') only when rows are simple (no embedded commas inside fields).
const fs = require('fs');
const readline = require('readline');
async function processFile(filePath) {
const fileStream = fs.createReadStream(filePath);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity,
});
const array = [];
for await (const line of rl) {
array.push(line.split(','));
}
return array;
}
processFile('example.csv')
.then((data) => console.log(JSON.stringify(data)))
.catch((err) => console.error(err));If example.csv on disk matches:
Name,Age,Job
Alice,30,Engineer
Bob,40,Doctorthen processFile logs one JSON string: the same header row plus two data rows as nested string arrays.
Node.js: csv-parser (row objects, streaming)
The csv-parser package turns each row into an object keyed by the header row and works well with pipe from a read stream.
npm install csv-parserconst fs = require('fs');
const csv = require('csv-parser');
const parsedData = [];
fs.createReadStream('large-example.csv')
.pipe(csv())
.on('data', (row) => parsedData.push(row))
.on('end', () => {
console.log(JSON.stringify(parsedData));
})
.on('error', (err) => console.error(err.message));Sample large-example.csv:
ID,Name,Age,Occupation
1,Alice,30,Engineer
2,Bob,40,Doctor
3,Charlie,50,TeacherWhen the stream finishes, you should see one JSON string: three row objects keyed by those headers (values remain strings unless you add conversion).
XMLHttpRequest (legacy)
Older codebases still use XMLHttpRequest. Prefer fetch in new code. The pattern is the same: read responseText, split rows, split fields, with the same caveats as manual parsing.
D3 (d3.csvParse for strings)
If you already ship D3 or d3-dsv, csvParse turns a CSV string into an array of objects (similar to Papa with header: true). d3.csv loads a URL instead; that is a different entry point.
Example with an ES module import (matches d3-dsv and modern D3 bundles):
import { csvParse } from 'd3-dsv';
const text = `Name,Age,Job
Alice,30,Engineer
Bob,40,Doctor`;
console.log(JSON.stringify(csvParse(text)));You should see one JSON string: two row objects with string field values (D3 does not apply dynamicTyping unless you add it yourself).
CSV → JSON string → array
Sometimes you round-trip through JSON for APIs or storage (JSON in JS, nested JSON search). The pattern below builds objects, stringifies, then parses again to show a clean separation of steps.
const csv = `Name,Age,Job
Alice,30,Engineer
Bob,40,Doctor`;
const rows = csv.split('\n');
const headers = rows[0].split(',');
const array = rows.slice(1).map((row) => {
const values = row.split(',');
const obj = {};
headers.forEach((header, index) => {
obj[header] = values[index];
});
return obj;
});
const json = JSON.stringify(array);
const jsonArray = JSON.parse(json);
jsonArray.forEach((row) => {
console.log(`Name: ${row.Name}, Age: ${row.Age}, Job: ${row.Job}`);
});You should see two console.log lines—one per person—with interpolated Name, Age, and Job.
Validate row width when you control the pipeline:
if (values.length !== headers.length) {
console.error('Row has incorrect number of fields:', row);
}JavaScript array to CSV (export)
Papa.unparse turns an array of rows back into a CSV string (each row is an array of cell values):
const Papa = require('papaparse');
const rows = [
['Name', 'Age'],
['Alice', 30],
];
console.log(Papa.unparse(rows));You should see one multi-line string: a header row followed by Alice and 30, with a newline between rows.
Regex-based parsing
Regular expressions can match delimited cells, but they become brittle with nested quotes or multiline fields (see “regex vs state machine” discussions for CSV). One older pattern flattens every cell, then re-slices by a fixed column count:
const csvString =
'Name,Age,"Address, Number",Job\nAlice,30,"123 St, Apt 4",Engineer';
const regex = /(".*?"|[^",\n]+)(?=\s*,|\s*\n|$)/g;
const array = [];
let m;
do {
m = regex.exec(csvString);
if (m) array.push(m[1].replace(/"/g, ''));
} while (m);
const perLine = 4;
const lines = [];
for (let i = 0; i < array.length; i += perLine) {
lines.push(array.slice(i, i + perLine));
}
console.log(JSON.stringify(array));
console.log(JSON.stringify(lines));You should see two JSON lines: first a flat list of eight cell strings, then a pair of four-column rows grouped from that flat list.
Prefer Papa or d3.csvParse before investing in custom regex.
Summary
Turning javascript csv to array data is almost always a three-step story: read raw text, split into rows, then split or parse each row into fields. People ask whether they can use String.split alone for csv javascript work; it is fine for toy data, but real csv to javascript inputs often include quoted commas, inconsistent line endings, or a UTF-8 BOM, which is why libraries like Papa Parse or d3.csvParse exist and why teams reach for them before writing custom parsers.
In the browser you normally combine a file input or fetch with one of those parsers; in Node you might stream very large files with readline, csv-parser, or Papa’s streaming API so memory stays bounded. If you are deciding between a 2D string array and an array of objects, pick objects when the first row is a stable header row you want as keys, and pick raw arrays when you need maximum speed or irregular columns.
References
- FileReader — MDN
- String.prototype.split — MDN
- Fetch API — MDN
- Papa Parse
- csv-parser on npm
- RFC 4180 (CSV)
