NDJSON & JSON Lines (JSONL): What It Is and How to Use It

Why a 2 GB log file isn't one big JSON array — what newline-delimited JSON is, when to use it, and how to read, write, and convert it.

The first time you try to JSON.parse a multi-gigabyte export and your process runs out of memory, you learn why nobody ships large datasets as a single JSON array. The answer most data tools reach for is NDJSON — newline-delimited JSON, also known as JSON Lines or JSONL. Here's what it is and how to work with it.

What is NDJSON?

NDJSON (Newline-Delimited JSON) is a text format where each line is one complete, independent JSON value — usually an object — separated by a newline (\n). There's no wrapping array and no commas between records. A file looks like this:

{"id": 1, "name": "Ada",   "role": "admin"}
{"id": 2, "name": "Linus", "role": "user"}
{"id": 3, "name": "Grace", "role": "user"}

Compare that to the equivalent JSON array, which wraps everything in [ ] and joins records with commas:

[
  {"id": 1, "name": "Ada",   "role": "admin"},
  {"id": 2, "name": "Linus", "role": "user"},
  {"id": 3, "name": "Grace", "role": "user"}
]

That one structural difference — independent lines vs. one wrapped document — is the whole point, and it's what makes NDJSON better for data at scale.

JSON Lines, JSONL, NDJSON — are they the same?

For practical purposes, yes. JSON Lines (extension .jsonl) and NDJSON (extension .ndjson) are near-identical specifications: one JSON value per line, newline-separated, UTF-8 encoded. The terms are used interchangeably across tooling, and a file written by one is readable by the other.

Why use NDJSON instead of a JSON array?

A JSON array is a single document: a parser must read the whole thing before it can hand you even the first record. NDJSON is a stream of records, and that changes everything about working with large or live data:

JSON arrayNDJSON / JSON Lines
ParsingMust load & parse the entire file at onceParse one line at a time — constant memory
AppendingRewrite the file (or splice before the ])Append a line — O(1), no rewrite
A bad recordOne syntax error breaks the whole parseIsolated to a single line; skip and continue
Best forSmall, complete documents; config; API bodiesLogs, large exports, pipelines, bulk loads

That's why NDJSON shows up in log aggregation, data pipelines, the Elasticsearch _bulk API, BigQuery and Athena load jobs, jq -c output, and LLM fine-tuning datasets — anywhere records arrive continuously or a dataset is too big to hold in memory.

How to read and write NDJSON in code

Because each line stands alone, you parse line by line — never the whole file. In JavaScript:

// Parse a small NDJSON string
const records = text
  .split("\n")
  .filter(Boolean)            // skip blank lines
  .map((line) => JSON.parse(line));

// Serialize records back to NDJSON (note: no array, no commas)
const ndjson = records.map((r) => JSON.stringify(r)).join("\n");

For files too big to hold in memory, stream them in Node.js a line at a time:

import { createReadStream } from "node:fs";
import { createInterface } from "node:readline";

const rl = createInterface({ input: createReadStream("data.ndjson") });
for await (const line of rl) {
  if (line.trim()) handle(JSON.parse(line));   // one record, constant memory
}

In Python, iterating a file already yields one line at a time:

import json

# Read — never loads the whole file into memory
with open("data.ndjson") as f:
    for line in f:
        if line.strip():
            record = json.loads(line)

# Write — one compact JSON value per line
with open("out.ndjson", "w") as f:
    for r in records:
        f.write(json.dumps(r) + "\n")

Common gotchas

  • It is not a JSON array. Don't wrap NDJSON in [ ] or put commas between lines — that breaks the format. Each line must parse on its own.
  • Keep each record on one line. Pretty-printed (multi-line) JSON objects are invalid in NDJSON. Serialize compact — JSON.stringify(r) with no indentation.
  • Handle blank lines. A trailing newline is fine, but filter empty lines before parsing so you don't call JSON.parse("").
  • UTF-8, one value per line. The value is usually an object, but any JSON value (number, string, array) on its own line is valid.

Convert between JSON and NDJSON without code

When I just need to flip a JSON array into NDJSON for a bulk load — or turn a log file back into an array to inspect it — a browser tool is faster than a script. Our free JSON to NDJSON converter goes both ways and runs entirely client-side, so your data never leaves the page (disclosure: I built it). If a line won't parse, run it through the JSON validator first, and use the JSON formatter to expand a single record for reading.

FAQs

What is NDJSON?
Newline-delimited JSON — a format where each line is one complete JSON value (usually an object), separated by \n, with no wrapping array. It's built for streaming and large datasets.

Is NDJSON the same as JSON Lines (JSONL)?
Effectively yes — .jsonl and .ndjson are near-identical (one JSON value per line, UTF-8) and the terms are interchangeable.

Why use NDJSON instead of a JSON array?
You can read/write one record at a time (constant memory), append in O(1), and a bad record only affects its own line — ideal for logs, pipelines, and bulk loads.

How do I convert a JSON array to NDJSON?
Write each element as one compact line: records.map(r => JSON.stringify(r)).join("\n") — don't wrap it in brackets. Or use the JSON ↔ NDJSON tool.

What file extension does NDJSON use?
.ndjson or .jsonl — both are plain UTF-8 text with one JSON value per line.

Related articles