mrkeyoor.com_
Sat 08 Aug 22:49 UTC
npmDataupdated 08 Aug 2026

csv-parser

csv-parser is a dependency-free CommonJS Transform stream that turns CSV bytes into one JavaScript object per row. It handles quoted fields, embedded newlines, custom separators, header and value mapping, strict column counts, row byte offsets, and backpressure through Node streams. It also installs a command named csv-parser that converts a file or standard input to newline-delimited JSON. The parser is fast and focused, but deliberately leaves encoding conversion, BOM removal, schema validation, and full-table collection to other code.

Verdict

One of the best small Node choices for fast, backpressure-aware CSV ingestion. Set strict and maxRowBytes deliberately, clean up encoding first, and avoid the currently nonfunctional CLI flags.

API stability4/5The core csv(options) Transform interface and header, separator, mapping, and strict options have remained recognizable throughout major version 3, which began in 2020. Version 3.2 added optional byte-offset output and 3.2.1 changed dangerous header handling for security, so output shape can still change when an option is enabled or a blocked header appears.
Docs3/5The README covers every parser option, stream events, encoding, BOM handling, benchmarks, the CLI, and practical header examples. Several details are stale or wrong in 3.2.1: the Node requirement disagrees with package.json, the API says it returns an array despite returning a Transform, and CLI help lists strict and remove flags that the current argument parser never handles.
Maintenance4/5Version 3.2.1 shipped in May 2026 with a targeted prototype-pollution fix, following byte-offset support in January 2025. The repo has a broad fixture and csv-spectrum test suite, but GitHub reports 64 open issues and pull requests, and much of the lint, coverage, and README badge configuration still references tooling from the Node 10 era.
Ecosystem5/5npm recorded 2,987,848 downloads in the latest measured week and the repository has 1,500 stars. It composes directly with Node streams, includes declarations and a CLI, and has established recipes for iconv-lite, strip-bom-stream, Browserify, and neat-csv. The csv-spectrum fixture coverage also gives users a shared compatibility baseline.

Use it if

  • You need to process a large CSV incrementally without loading the whole file into memory
  • Your Node pipeline already uses readable, transform, and writable streams
  • You want header normalization and lightweight value conversion while each row is parsed
  • You need byte offsets so failed or indexed rows can be traced back to the source stream
Skip it if

Setup reality

`npm install csv-parser` gives you a CommonJS parser, TypeScript declarations, and the csv-parser executable. It requires Node 10 or newer according to package.json, although the README still says Node 8.16, so trust the package engine field. The happy path is a file stream piped into csv(), but production setup needs limits and input cleanup. Values are strings unless mapValues converts them. The first row becomes headers unless you pass headers, and supplying headers does not consume an existing header row, so add skipLines: 1 when both are present. strict defaults to false; short rows can silently shift meaning around missing middle columns, while long rows are placed under keys such as _10. Set strict: true when column count is contractual. maxRowBytes defaults to Number.MAX_SAFE_INTEGER and should be capped for untrusted files. UTF-8 BOMs are not removed, and non-UTF-8 input needs a transcoding stream such as iconv-lite before the parser. Separator, quote, escape, and custom newline options are reduced to their first byte, so multi-character or multibyte delimiters do not work. Version 3.2.1 blocks prototype-pollution keys by dropping columns named __proto__, constructor, or prototype, which is safer but can surprise consumers of legitimate files with those headers. Always handle pipeline errors and consume rows with a pipeline, async iterator, or writable stream so backpressure remains effective. The CLI emits NDJSON, not a JSON array, and its current --strict and --remove help entries do not match the argument parser implementation.

Patterns

Parse a CSV file row by rowparse-csv-file

const fs = require('node:fs');
const csv = require('csv-parser');

fs.createReadStream('data.csv')
  .pipe(csv())
  .on('data', (row) => console.log(row))
  .on('error', (error) => console.error(error))
  .on('end', () => console.log('done'));

Every value is a string by default, and the first row is consumed as headers rather than emitted as data.

Consume rows with an async iteratorconsume-with-async-iterator

const input = fs.createReadStream('data.csv').pipe(csv());

for await (const row of input) {
  await saveRow(row);
}

Sequential awaiting preserves backpressure. An error from the file or parser rejects the loop.

Use numeric keys when the file has no headersparse-without-header-row

fs.createReadStream('rows.csv')
  .pipe(csv({ headers: false }))
  .on('data', (row) => {
    console.log(row['0'], row['1']);
  });

headers: false also disables strict mode internally, so validate the numeric columns yourself.

Supply headers and skip the file's header linesupply-known-headers

const parser = csv({
  headers: ['name', 'age'],
  skipLines: 1,
  strict: true,
});

fs.createReadStream('people.csv').pipe(parser);

Without skipLines: 1, the original header row is emitted as ordinary data under the supplied names.

Normalize headers and drop a columnnormalize-and-drop-headers

const parser = csv({
  mapHeaders: ({ header }) => {
    const key = header.trim().toLowerCase();
    return key === 'internal_notes' ? null : key;
  },
});

Returning null removes both the header and every value in that column. Version 3.2.1 also drops __proto__, constructor, and prototype headers.

Convert selected values while streamingconvert-column-values

const parser = csv({
  mapValues: ({ header, value }) => {
    if (header === 'age') return Number(value);
    if (header === 'active') return value === 'true';
    return value;
  },
});

Number('') is 0 and invalid numbers become NaN. Add explicit validation when empty and malformed cells have different meanings.

Reject mismatched or oversized rowslimit-untrusted-rows

const { pipeline } = require('node:stream');
const parser = csv({
  strict: true,
  maxRowBytes: 1024 * 1024, // 1 MiB
});

pipeline(
  fs.createReadStream('upload.csv'),
  parser,
  destination,
  (error) => { if (error) reportImportFailure(error); }
);

The default row limit is Number.MAX_SAFE_INTEGER. strict catches column-count mismatches but does not validate types or required values.

Parse tab-separated dataparse-tsv

fs.createReadStream('data.tsv')
  .pipe(csv({ separator: '\t' }))
  .on('data', processRow);

The separator is one byte. Multi-character separators and multibyte Unicode delimiter characters are not supported.

Skip a preamble and comment linesskip-comments-and-preamble

const parser = csv({
  skipLines: 2,
  skipComments: '#',
});

fs.createReadStream('export.csv').pipe(parser);

CSV comments are non-standard. A comment is recognized only when its marker begins the physical line.

Remove a UTF-8 BOM before parsingstrip-utf8-bom

const stripBom = require('strip-bom-stream');

fs.createReadStream('excel-export.csv')
  .pipe(stripBom())
  .pipe(csv())
  .on('data', processRow);

csv-parser does not remove a BOM itself; without this step the first header can contain an invisible leading character.

Track each row's source byte offsetemit-row-byte-offsets

fs.createReadStream('data.csv')
  .pipe(csv({ outputByteOffset: true }))
  .on('data', ({ byteOffset, row }) => {
    indexRow(byteOffset, row);
  });

This option changes every emitted chunk from a row object to `{ byteOffset, row }`; update TypeScript and downstream transforms accordingly.

Convert CSV to NDJSON with the CLIconvert-to-ndjson

npx csv-parser input.csv > output.ndjson
# or from stdin
node produce-csv.js | npx csv-parser -s ';' > output.ndjson

The output is one JSON object per line, not a JSON array. In 3.2.1, avoid the advertised --strict and --remove flags because the CLI parser does not implement them.

Alternatives

PackageRegistryPick it when
csv-parsenpmUse it for a larger option set, sync and callback APIs, casting controls, and richer CSV dialect handling
@fast-csv/parsenpmUse it when you want TypeScript-first stream parsing with validation and row transformation hooks
papaparsenpmUse it when browser support, delimiter detection, worker parsing, or browser file inputs matter
neat-csvnpmUse it for small inputs when a Promise returning all parsed rows is more convenient than streaming