Convert CSV to NDJSON

Converting CSV to NDJSON writes one JSON object per line, with the header row supplying the keys, so the file can be concatenated, split and read a record at a time. Unlike a CSV, two NDJSON files joined end to end are still a valid file. The conversion runs in your browser.

  • Where it runs In your browser. The file is never uploaded.
  • Lossless Nothing is discarded. The NDJSON holds exactly what the CSV held.
  • File size limit Up to 100 MB per file, free, without an account.

Up to 100 files at once. Mixed formats are fine.

The header row moves into every line

A CSV names its columns once, at the top, and every row after that is positional — the third field is the city because the third heading said so. NDJSON does the opposite: each line is a complete object carrying its own keys, so the seventy-thousandth record is as intelligible on its own as the first.

That is the trade the conversion makes, and it is not free. Repeating the key names on every line makes the file larger, sometimes considerably so on a table of short values with long headings. What you buy is that no line depends on any other line, which is what makes every other property on this page possible.

Two CSV exports that disagree about their columns

The reason this conversion is usually asked for is not elegance. It is that you have eleven monthly exports, the system added a field in April, and there is no way to concatenate a file with fourteen columns and a file with fifteen. Joining the CSVs leaves a header row sitting in the middle of the data, and even after removing it the fields no longer line up.

NDJSON has no such problem. Records with different key sets sit in the same file quite legitimately, and every reasonable consumer — a query engine, a load job, a script — treats a missing key as absent rather than as a broken row. Convert each export separately, concatenate the results with `cat`, and the schema difference becomes a fact about individual records instead of a structural failure.

Splitting, appending and resuming a converted export

Because a record ends where the line ends, the file can be cut anywhere a newline occurs and both halves stay valid. `split -l 100000` produces loadable chunks. `head -n 1000` produces a sample you can actually run against a schema. A failed load can be resumed from a line number instead of started again.

Appending works for the same reason: new records go on the end with `>>` and nothing above them has to be rewritten. Doing the equivalent to a CSV means checking whether the header is present, whether the column order matches and whether the last line ended with a newline, and getting any of those wrong corrupts the file silently rather than loudly.

Types come from the CSV parser, and it is guessing

JSON distinguishes the number 7 from the string "7" and a CSV does not, so something has to decide, and what decides is inference over the text. Values that parse as numbers become JSON numbers — `1e5` arrives in the output as 100000 — while `true` and `false` become booleans and everything else stays a string.

The guess is right for quantities, prices and flags and wrong for identifiers. `007` becomes `7`, and once it is a number in the JSON there is nothing left to recover it from. If the export contains codes rather than measurements, the cheapest defence is to make them unambiguous in the source: a prefix, or a column already exported as text, both survive. The general test is whether adding two values in the column would mean anything; if not, it is an identifier and it wanted to stay a string.

An empty CSV field becomes null

A blank cell is written as `null` rather than as an empty string. That is a real decision and worth knowing, because the two are not the same to anything that consumes the file: a JSON schema with `"type": "string"` rejects null, and a database load will put a null where you may have expected the empty string you saw in the source.

What the conversion cannot do is tell you which one the export meant. A CSV has exactly one way to write "nothing here" and it is used for both "this field is blank" and "this field does not apply". If the distinction matters downstream, it has to be encoded in the source explicitly — a sentinel value, or a second column — before any conversion sees it.

Reading the result with jq and other line tools

The file is designed for `jq -c`, which reads it a value at a time without holding the whole thing in memory. `jq -r ".city"` over a converted export prints one city per line; `jq -s "length"` counts records; a `select` filter produces a smaller NDJSON file that is itself still valid input to everything else.

The same applies to anything that reads lines. `grep` on the raw file works and is honest, because a record cannot span two lines and cannot be broken across a match. `wc -l` gives the record count exactly, which is a small thing until you compare it to counting rows in a CSV where a quoted address contains a newline.

How much bigger the NDJSON is than the CSV

Expect growth. Every line repeats the key names, every string is quoted, and the punctuation of an object is added around the values. On a table of short codes with descriptive column headings the file can double; on a table of long free-text fields the difference is slight, because the values dominate.

Compression closes most of the gap, since the repeated keys are exactly what a compressor is best at, and a gzipped NDJSON file is usually close to a gzipped CSV of the same data. If the size at rest is what actually matters rather than the streaming shape, that is a signal to look at Parquet instead, where the column names are stored once in a footer rather than on every record.

What still has to happen before a load job accepts it

NDJSON is a container shape, not a contract. A destination that ingests JSON lines will still have opinions about field names, about date formats, and about whether a null is permitted in a given position, and none of those are decided by this conversion.

The two that catch people out are dates and identifiers. A date in a CSV is text and stays text, so whatever string the exporting system chose is what arrives — and if that string is `03/04/2024` the ambiguity travels with it. Identifiers, as above, may have become numbers. Both are cheap to fix in a one-line `jq` pass over the converted file, and much more expensive to fix after the load.

The export is parsed on this page, not on a server

Reading the CSV and writing the JSON lines are both plain JavaScript, loaded by this page on demand. No request carries the file, so a nightly customer export or a finance extract can be converted without it becoming somebody else’s copy.

The free tier stops at 100 MB a file, and under that the ceiling is memory. The whole table is built before anything is written, which puts tens of megabytes comfortably in range and the last stretch to 100 MB at the point where a browser tab starts to strain. For a larger export, a streaming CSV reader in a script is the right tool — and given that the destination is a stream, writing that script is often the correct end state anyway.

When JSON, CSV or Parquet is the better destination

Choose a plain JSON array instead when the whole thing is going to be loaded at once by something that expects an array — an API payload, a test fixture, a configuration file. NDJSON is worse than useless there, because most JSON parsers will reject it outright on the second line.

Keep the CSV if the destination is a spreadsheet or an import screen that names it, since neither gains anything from records on lines. And choose Parquet when the same data will be queried repeatedly rather than ingested once: it stores the column names once, keeps the types explicitly instead of by inference, and is a fraction of the size on disk.

How to turn a CSV export into NDJSON

  1. Drop the CSV export onto this page, or click to choose one.
  2. It is parsed and rewritten as one JSON object per line, in your browser.
  3. Download the NDJSON and concatenate, split or stream it as needed.

CSV and NDJSON: a header row against self-describing lines

CSV compared with NDJSON
CSVNDJSON
Full nameComma-Separated ValuesNewline-Delimited JSON
File extension.csv.ndjson, .jsonl
Media typetext/csvapplication/x-ndjson
First published19722013
SpecificationRFC 4180
LicensingOpen standardOpen standard
Standing todayCurrentCurrent
Opens in a browserNo browserNo browser
Considered insteadXLSX, JSON, ParquetJSON

What survives

Nothing is discarded. CSV and NDJSON both store their content losslessly, so the conversion is a change of packaging rather than a change of quality, and it can be repeated without accumulating damage.

Opening the result

pandas reads both CSV and NDJSON, so there is a way to check the result against the original without a second tool.

What each format is for

CSV was published in 1972. The specification is RFC 4180, and it is worth reading if the file has to outlive the tool that wrote it.

NDJSON dates from 2013. jq and pandas all read it.

CSV was published in 1972 and NDJSON in 2013. The older one is generally the safer file to hand to somebody; the newer one usually does the job in fewer bytes.

CSV to NDJSON: types, concatenation and line-based tools

Are my CSV files uploaded anywhere?

No. This conversion runs entirely inside your browser, so the file never leaves your device. You can confirm it yourself: open the network tab of your browser's developer tools and convert something. You will see the page load, plus the analytics and advertising the site is paid for with — and nothing carrying your file.

What is the difference between NDJSON and JSONL?

Nothing meaningful. Both names describe one complete JSON value per line with no surrounding array, and the .ndjson and .jsonl extensions are used interchangeably. Tools that accept one accept the other.

Can I concatenate two of these files?

Yes, and that is most of the point. Two NDJSON files joined end to end are a valid NDJSON file, which is not true of CSV — joining two CSVs leaves a header row stranded in the middle of the data.

Do the values keep their types?

They get the types the CSV parser infers. Numeric-looking values become JSON numbers, true and false become booleans, empty fields become null, and everything else stays a string. A CSV carries no types of its own, so inference is the only option available.

What happens to leading zeros?

They are lost, because a value like 007 read as a number is 7. Postcodes, part numbers and zero-padded account references are the usual casualties, and the JSON will contain a number where you expected a string.

Is the output pretty-printed?

No, and it must not be. Each record is written on exactly one line with no internal line breaks, which is what makes the format readable a line at a time. Indented JSON would break every consumer of it.

Does the CSV leave my machine?

No. Both the parsing and the writing are plain JavaScript running on this page, so no request carries the file. The only size rule is ours rather than a server’s: 100 MB a file, and a hundred files to a batch.

More about these formats