jsonlkit.com
JSONL (JSON Lines) utilities, in the browser
Say hi →

CSV to JSONL Converter

updated 30 May 2026

CSV to JSONL converter. Turn a CSV with a header row into JSONL (JSON Lines), one JSON object per row. Auto-detects numbers and booleans, supports dot.notation for nested keys. Up to 1 GB, runs in your browser, nothing uploaded.

Need the other direction? → JSONL → CSV

100% client-side. Your CSV stays in your browser.

⌨ Working with big CSVs? jsonlkit from csv data.csv > out.jsonl — streaming RFC 4180 parser, no file-size limit.

Convert

Drop a .csv file here, or

CSV to JSONL Converter

Turn a CSV file (with a header row) into JSONL — one JSON object per row, ready for fine-tune uploads, BigQuery loads, or any tool that wants newline-delimited JSON. Numbers and booleans get auto-typed.

What this tool does

It reads a CSV — a header row on line 1, then one record per row — and rewrites it as JSONL: one minified JSON object per line. The header cells become the JSON keys, each data row becomes one object, and cell values are typed for you (a bare 42 becomes the number 42, true becomes a boolean, null becomes null). The CSV parser is RFC 4180-aware, so quoted cells, doubled "" escapes, and newlines inside quoted cells all survive. Everything runs in your browser; nothing is uploaded.

The intent this closes is the mirror image of flattening: "I built my data in a spreadsheet and now I need it as newline-delimited JSON." That destination is almost always an LLM fine-tune upload (OpenAI, Anthropic, HuggingFace), a BigQuery or Snowflake load, a vector-database bulk import (Pinecone, Weaviate, Qdrant), or a streaming pipeline that wants one JSON object per line.

When you'd reach for it

How the conversion works

Three stages run on every click of Convert.

1. Parse the CSV

The whole input is parsed with an RFC 4180 reader. A cell wrapped in double quotes may contain the delimiter, line breaks, and doubled quotes ("" → a literal "); CR, LF, and CRLF line endings are all normalised. One logical CSV row — even one that spans several physical lines because of a quoted newline — becomes one JSONL object. If the parser can't make sense of the input it stops with Failed to parse CSV rather than emitting half a file.

2. Read the header, then type each cell

Row 1 is the header. Each name becomes a JSON key. Empty header cells and duplicate header names are flagged as header issues in the status bar — the run still completes, but a duplicate name means the last column with that name wins and the earlier one is overwritten.

Every data cell is then coerced according to Types. Auto-typing is deliberately strict: only values matching a clean number pattern become numbers, so 007, "1,000", and " 42 " all stay strings on purpose — turning them into numbers would quietly corrupt real-world identifiers.

3. Build each object and emit

For each row the converter walks the columns in header order. A blank row (one empty field) is skipped. With Nesting flat, a header like user.name becomes a literal key "user.name". With Nesting expand dot.notation, that same header is split on dots and rebuilt as {"user":{"name":…}}. Each object is then JSON.stringify'd to one minified line. The status bar reports how many rows converted.

Options reference

Delimiter

The character between columns in your input. Comma is the default. Semicolon matches European Excel exports that use comma as a decimal mark. Tab is the safe choice when cells contain commas, prose, or embedded JSON — tabs almost never collide with real values. Pipe for files from older ETL tools. This must match what your file actually uses; pick the wrong one and the whole row lands in a single key.

Types

CSV is string-only on the wire, so this controls how aggressively values are converted. auto turns clean integers, decimals, and the literal words true/false/null into real JSON types and drops empty cells from the object entirely. auto + blanks → null does the same typing but writes an explicit null for an empty cell instead of omitting the key — use it when a downstream consumer requires every field on every line. strings only leaves every value a string and keeps empty cells as ""; reach for it with IDs that look numeric but must not be (zip codes, leading-zero SKUs, phone numbers).

Nesting

flat keys uses each header verbatim, so a user.name column yields {"user.name":"Ada"}. expand dot.notation treats dots as a path and rebuilds nested objects: {"user":{"name":"Ada"}}. Expansion only kicks in for headers that actually contain a dot. If two expanded paths collide, the last value written wins.

Output format

Example

Input (CSV with a dot.notation header):

id,user.name,user.age,tags
1,Ada,36,"eng,lead"
2,Linus,54,eng

Output (JSONL, Types auto, Nesting expand dot.notation):

{"id":1,"user":{"name":"Ada","age":36},"tags":"eng,lead"}
{"id":2,"user":{"name":"Linus","age":54},"tags":"eng"}

The user.name and user.age columns merged into one nested user object, id and age became real numbers, and the quoted "eng,lead" kept its comma instead of splitting into two columns. This is the round-trip pair to JSONL → CSV — flatten with one, restore with the other.

Recipes by intent

Build an OpenAI / Anthropic fine-tune file from a Sheet

Keep columns like system, user, assistant. Types auto, Nesting flat. You'll get one flat object per row. If your provider wants the messages array shape, reshape with a quick jq pass, then confirm against the OpenAI or Anthropic validator.

Load into BigQuery or Snowflake

Types auto so numbers and booleans land as real types rather than strings. JSONL is the recommended ingestion format for nested schemas, which plain CSV can't express at all.

Rebuild nested structure from a flattened CSV

If the file came from JSONL → CSV with dotted headers, set Nesting to expand dot.notation. Columns like address.city and address.zip fold back into one address object.

Preserve leading-zero IDs

Types strings only. Every value stays a string, so 00123, +44 20…, and zip codes survive intact instead of being coerced to numbers.

Force every key onto every line

Types auto + blanks → null. Empty cells become explicit null values, so a strict loader that expects a fixed set of keys per record doesn't trip on missing fields.

Limits and performance

Errors and how to fix them

Failed to parse CSV

The reader hit something it couldn't resolve — almost always an unclosed quote. A cell that opens with " but never closes it swallows the rest of the file. Open the input in a CSV-aware editor and find the unmatched quote, or escape literal quotes inside a cell by doubling them ("she said ""hi""").

CSV is empty

There was no parseable content. Check that you pasted the data (header row included) and that the delimiter you picked matches the file.

duplicate header 'X' / header column N is empty

Two columns share a name, or a header cell is blank. The run still completes, but a duplicate name means the last column wins and overwrites the earlier one, and a blank header cell drops that column. Rename the columns on line 1 so each key is unique and non-empty.

Some of my "numbers" stayed as strings

Auto-typing only converts strictly-formatted numbers — no leading zeros (except a bare 0), no surrounding spaces, no thousands separators. 007, "1,000", and " 42 " all stay strings on purpose, because coercing them tends to corrupt real data. Strip the spaces or commas upstream if you genuinely want numbers.

The first object is missing fields

Line 1 has to be the header row, not data. Without a header the first row's values would become the keys and you'd lose that record. Prepend a header row in any editor.

My commas ended up inside the wrong columns

Either the delimiter setting doesn't match the file, or a cell with a comma wasn't quoted. Wrap any value containing the delimiter in double quotes ("hello, world"), or switch the source export to Tab-delimited.

Output looks like garbage characters

Files saved from Excel on Windows are sometimes UTF-16 with a BOM. Re-save as "CSV UTF-8" before pasting.

FAQ

Does the first row have to be a header?

Yes. The header row supplies the JSON keys. Without it you'd get {"1":"Ada","2":36} on the first record and lose the meaning of every column. If your file has no header, prepend one in any text editor.

Are CSV cells with embedded newlines supported?

Yes. The parser follows RFC 4180, so a quoted cell can span multiple physical lines. Each logical CSV row still produces exactly one JSONL line.

Are arrays supported?

Not natively — CSV cells are scalar. Store an array as a JSON string inside a cell ("[1,2,3]") and post-process the JSONL, or use expand dot.notation with indexed headers (tags.0, tags.1) the way JSONL → CSV writes them. Note that expansion rebuilds nested objects with numeric string keys, not true JSON arrays.

What's the difference between dropping a blank and writing null?

In Types auto an empty cell is omitted, so the key simply isn't present in that object. In auto + blanks → null the key is present with value null. Choose the second when a consumer needs a consistent set of keys on every line.

Is my CSV uploaded anywhere?

No. The parser runs entirely in your browser — you can disconnect from the network and it still works. That makes it safe for customer lists, PII, and private training data.

Related tools