JSON Lines (NDJSON): what it is and when to use it
One JSON value a line. Why logs and data exports use it, and how to read, write and convert it.
JSON Lines is a file where each line holds one complete JSON value.
{"id": 1, "event": "login", "at": "2026-11-04T09:00:00Z"}
{"id": 2, "event": "view", "at": "2026-11-04T09:00:04Z"}
{"id": 3, "event": "logout", "at": "2026-11-04T09:12:31Z"}
That is the whole format. No outer brackets, no commas between the records.
It also goes by the names NDJSON (newline delimited JSON) and JSONL,
and the file usually ends in .jsonl or .ndjson.
Why it exists
A normal JSON file holds one value. If that value is a list of ten million
records, a reader must read the whole file before it can use the first record,
because the file is not complete until the final ] arrives.
JSON Lines turns that around. Each line is complete on its own. So:
- A program can start work at once. Read a line, use it, forget it. The memory stays flat whatever the size of the file.
- A file can be written for ever. A log file is never finished. You cannot write the closing bracket of a list that is still growing.
- Ordinary tools work.
wc -lcounts the records.headshows the first ones.grepfinds them.splitcuts the file into pieces that are all still valid. - A broken file is still mostly useful. If the disk fills while writing, every complete line before the fault is still readable. A cut-off JSON array gives you nothing without a repair.
Who uses it
- Log files. Almost every structured logger writes this form.
- Data exports. BigQuery, Snowflake and most data warehouses import and export it.
- Machine learning data sets. Training files are nearly always JSONL.
- Streaming APIs. An API that sends results as they come writes one JSON value a line.
The rules
- One value on each line. It does not have to be an object, but in practice it almost always is.
- No line break inside a record. This is the rule people break. A record
must be on one line, so the writer must not pretty-print it. A
\ninside a text value is fine, because it is written as the two characters\andn, not as a real line break. - UTF-8, and
\nas the line separator. - An empty line is usually allowed and ignored. Do not depend on it.
Reading it
On the command line
wc -l events.jsonl # how many records
head -3 events.jsonl # look at the first three
grep '"event": "error"' events.jsonl | head
jq -c 'select(.event == "error")' events.jsonl
jq -s '.' events.jsonl > events.json # make it one array
In this viewer. Open the file and the tool sees the form by itself: it shows the records as a list, with an index for each one. Everything else then works as usual — the tree, the search, JSONPath, the table view and the conversion to CSV. Press Format and you get one proper JSON array, which is the usual way to turn JSONL into JSON.
In Python
import json
with open('events.jsonl') as f:
for line in f:
if line.strip():
record = json.loads(line)
process(record)
In Node
import { createReadStream } from 'node:fs'
import { createInterface } from 'node:readline'
const lines = createInterface({ input: createReadStream('events.jsonl') })
for await (const line of lines) {
if (line.trim()) process(JSON.parse(line))
}
Writing it
The one rule that matters: do not pretty-print.
import json
with open('out.jsonl', 'w') as f:
for record in records:
f.write(json.dumps(record) + '\n') # no indent
for (const record of records) {
stream.write(JSON.stringify(record) + '\n') // no second argument
}
Flush or close the file at the end. A half-written last line is the most common fault in a JSONL file.
When not to use it
- A configuration file. It holds one object, not a list. Use plain JSON.
- A message in an API. A single answer is one value. Use plain JSON.
- A deeply nested document. If each record is 50 kB of nested objects, you lose the one benefit of the form, because a line is no longer small.
JSON Lines against CSV
Both hold one record a line, and people often choose between them.
| JSON Lines | CSV | |
|---|---|---|
| Nested data | Yes | No |
| Types | Text, number, true, false, null | Everything is text |
| Each record names its fields | Yes | Only the header does |
| Size | Bigger, keys repeat | Smaller |
| A spreadsheet opens it | No | Yes |
Choose CSV when the data is a flat table and a person will open it in a spreadsheet. Choose JSON Lines when records are nested, or when the fields differ from record to record.
The viewer converts between them: open a JSONL file and use Tools → To CSV.
Try it on your own file. The tool runs in your browser. Nothing is uploaded, and nothing is stored on a server.
Open the JSON ViewerRead next
How to compare two JSON files
Why a plain text diff fails on JSON, what a real JSON comparison does, and how to read the result.
How to turn JSON into CSV
The rules for flattening nested data into columns, the traps, and three ways to do it.