JSON Lines and NDJSON Formatter

Paste newline-delimited JSON to check every record, normalise it to one compact object per line, or expand each record so a long log stream becomes readable.

Input

Settings

History

Load from URL

What JSON Lines is and why it exists

JSON Lines (also called NDJSON or LDJSON) stores one complete JSON value per line, separated by newlines instead of being wrapped in an outer array. That small change makes the file streamable: a consumer can read, process and discard one record at a time without loading everything into memory, and a producer can append a record without rewriting the file. You meet it in structured application logs, BigQuery and Snowflake exports, Elasticsearch’s _bulk API, OpenAI fine-tuning and batch files, docker logs piped through jq -c, and data pipelines that pass events between jobs. The JSON Lines vs JSON guide covers when each layout is the better choice.

Formatting a .jsonl file in the browser

Drop the file onto the editor or paste a chunk of a log. The format is recognised from its shape, so a stream of objects lands here rather than in the plain JSON formatter. The output refreshes while you type; for very large pastes press Ctrl/Cmd+Enter to run it once. Ctrl/Cmd+Shift+M forces the compact layout and Ctrl/Cmd+Shift+C copies the result.

Besides the code view there are two others. Tree shows the stream as an array, one node per record, so you can fold nested payloads. Table turns the records into rows with one column per key, which is the quickest way to scan a few thousand log events for a failing request; clicking a row jumps to its line in the input. The Tools button adds JSONPath and jq queries, schema validation and key statistics over the whole stream. Parsing is done by a lossless line-by-line JSON parser running locally, so production logs with customer emails or tokens are never uploaded.

The Records option and the toolbar

  • Records has two settings. One compact record per line (valid NDJSON) strips all insignificant whitespace and writes every record on exactly one line, which is what loaders and _bulk endpoints expect. Pretty-print each record (for reading) expands each record over several indented lines; the result is easier on the eyes but is no longer valid JSON Lines, so switch back before saving.
  • Indent (2 spaces, 4 spaces or tabs) only affects the pretty-printed setting.
  • Sort keys orders object keys alphabetically inside every record, nested objects included. Records keep their original order, which matters for logs and for files where line order means time.
  • Minify always produces the compact layout, whatever Records is set to.

Numbers are copied from the input as written, so a 19-digit order ID or a price written as 10.50 comes out unchanged, and non-ASCII text stays readable instead of being turned into \u escapes.

How messy input is handled

The parser reads the input as a sequence of JSON values rather than splitting blindly on newlines. That makes it forgiving in useful ways:

  • A record that was pretty-printed across several lines is accepted and joined back onto one line, with an info note naming the line it started on.
  • Two records that ended up on the same line are separated onto their own lines.
  • Blank lines between records are dropped.
  • A top-level JSON array counts as one record and stays on a single line. To explode an array into one line per element, use JSON to NDJSON; to go the other way, NDJSON to JSON wraps the records in an array.

When a record is broken, every bad line is reported (up to 50), each message prefixed with “Record on line N” so you can jump straight to it. A truncated last line, which is common when a log is copied while still being written, shows up as an unexpected end of input on that line.

Examples

Order export with loose spacing

The spaces inside each object are removed so every order fits on one tight line, ready for a bulk import.

Input
{ "id": "ord_1001", "customer": "c_204", "total": 59.9, "status": "paid" }
{ "id": "ord_1002", "customer": "c_311", "total": 12.5, "status": "refunded" }
{ "id": "ord_1003", "customer": "c_204", "total": 230, "status": "paid" }
Output
{"id":"ord_1001","customer":"c_204","total":59.9,"status":"paid"}
{"id":"ord_1002","customer":"c_311","total":12.5,"status":"refunded"}
{"id":"ord_1003","customer":"c_204","total":230,"status":"paid"}
Open this example in the tool

Analytics events expanded for reading

With Records set to pretty-print, each event and its nested object is spread over indented lines.

Input
{"user":"u_17","event":"signup","plan":"pro","utm":{"source":"newsletter","campaign":"sept"}}
{"user":"u_18","event":"login","device":{"os":"iOS","version":"18.1"}}
Output
{
  "user": "u_17",
  "event": "signup",
  "plan": "pro",
  "utm": {
    "source": "newsletter",
    "campaign": "sept"
  }
}
{
  "user": "u_18",
  "event": "login",
  "device": {
    "os": "iOS",
    "version": "18.1"
  }
}
Open this example in the tool

Pretty-printed records pasted from a debugger

Each multi-line record is folded back onto a single line and an info note reports where it was joined.

Input
{
  "id": "evt_1",
  "type": "deploy",
  "service": "checkout"
}
{
  "id": "evt_2",
  "type": "rollback",
  "service": "checkout"
}
Output
{"id":"evt_1","type":"deploy","service":"checkout"}
{"id":"evt_2","type":"rollback","service":"checkout"}
Open this example in the tool

Records with keys in different orders

Sorting keys gives every line the same key order, which makes two exports far easier to diff.

Input
{"status":"paid","id":"ord_1001","total":59.9}
{"total":12.5,"id":"ord_1002","status":"refunded"}
Output
{"id":"ord_1001","status":"paid","total":59.9}
{"id":"ord_1002","status":"refunded","total":12.5}
Open this example in the tool

Common errors and how to fix them

ErrorCauseFix
Record on line 2: Trailing comma before '}'
Explained
A record ends with a comma after its last property, often from hand editing or a template that joins fields carelessly.Delete the comma before the closing brace on the reported line.
Record on line 2: Object keys must use double quotes, not single quotes
Explained
The line was written by code that printed a Python dict or a JavaScript object literal instead of serialising JSON.Emit the record with json.dumps or JSON.stringify so keys and strings use double quotes.
Record on line 2: Unexpected end of input: 1 bracket is still open — '{' opened at line 1
Explained
The last record is cut off, typically because the file was copied while the writer was still flushing it.Remove the incomplete final line or re-export the file once the writer has finished.
Record on line 2: Unexpected word 'not' — strings must be in double quotesA plain-text line such as a stack trace or a log banner is mixed into the JSON stream.Filter non-JSON lines out first, for example with grep, or move them into a field of their own record.

Frequently asked questions

What is the difference between JSONL and NDJSON?

Nothing practical. JSON Lines (.jsonl) and newline-delimited JSON (.ndjson) describe the same layout: UTF-8 text with one JSON value per line. LDJSON is a third name for it.

Can I open a large .jsonl log file?

Yes. Everything runs in a background worker in your browser, so files of tens of megabytes work; above a size threshold the tool waits for you to press Format instead of reformatting on every keystroke.

Is the pretty-printed output still valid JSON Lines?

No. Once a record spans several lines, line-based readers will fail on it. Use the pretty setting to inspect data and the compact setting for anything you save or send.

Why does my JSON array show up as a single line?

A top-level array is one JSON value, so it is one record. Convert it with JSON to NDJSON if you want each element on its own line.

Does sorting keys change the order of the records?

No. Only the keys inside each object are reordered; the records stay in the order they appeared.

Related tools