What the converter actually does
CSV looks like the simplest format in the world until you meet a real file. A
spreadsheet export with an address column, a free-text notes field, or a European
locale will break any converter built on split(",") — usually without
saying so. The output looks plausible, the row count is right, and the data is
quietly wrong.
This converter implements RFC 4180, the specification that actually describes the format. It reads the file one character at a time while tracking whether it is inside a quoted field, which is the only way to get the difficult cases right.
The three cases that break naive converters
Commas inside a field
A field wrapped in double quotes may contain the delimiter. The line
"Hopper, Grace",[email protected] is two fields, not three.
Splitting on commas gives you three, shifts every column after it, and puts an email
address in the age column.
Quotes inside a quoted field
RFC 4180 escapes a literal quote by doubling it. The field
"She said ""no"" twice" holds the text
She said "no" twice. Converters that strip quotes with a regular expression
turn this into fragments.
Line breaks inside a field
This is the one that surprises people. A quoted field can contain a newline — a
multi-line address or a comment field will have them. That means
you cannot split the file into rows before parsing, because a row is
not the same thing as a line. Any tool that starts with
text.split("\n") is already wrong before it looks at a single field.
Delimiter detection, and why frequency is the wrong test
Not every CSV uses commas. Files produced in Brazil, Germany, France and most of continental Europe use semicolons, because the comma is the decimal separator there and Excel adapts to the system locale. Tab-separated files are common in exports from databases and analytics tools.
The obvious detection method — count each candidate and pick the most frequent — fails on exactly the files where it matters. A semicolon-separated export with a free-text column full of prose will contain far more commas than semicolons, so frequency picks the comma and the whole file collapses into one column.
We score by consistency instead. The real delimiter produces the same count on every line, because every row has the same number of columns. Commas scattered through prose produce a different count on each line. Scoring how uniform the count is across the first twenty rows, rather than how large it is, identifies the true separator even when it is far less frequent. Quoted sections are skipped during counting for the same reason.
Type inference without data loss
JSON distinguishes strings, numbers, booleans and null; CSV has only text. Converting
36 to a number is usually what you want. Converting 007 to a
number is data loss, and it is the single most common way a conversion silently
corrupts a file.
Our rule is a round-trip test: a value becomes a number only if formatting that number back to text produces the original string exactly. So:
42→42, becauseString(42)is"42".007stays"007", becauseString(7)is"7".1.50stays"1.50", because the trailing zero would vanish.+55 11 98765-4321stays a string — it is not a number at all.1e5becomes100000only when it round-trips; otherwise it stays text.
This keeps ZIP codes, phone numbers, SKUs, bank account numbers and version strings
intact. true, false and null convert to their
JSON equivalents, and an empty cell becomes null rather than an empty
string, since that is what most consumers expect. If you would rather keep everything
as text, turn inference off.
Three output shapes, and when to use each
| Shape | Looks like | Use when |
|---|---|---|
| Array of objects | [{"name":"Ada","age":36}] | The default. Feeding an API, a JS app, or anything that reads by field name. |
| Array of arrays | [["name","age"],["Ada",36]] | Position matters more than names, or the file has no meaningful header. |
| NDJSON | One object per line, no wrapping array | Streaming into BigQuery, Elasticsearch, or any line-by-line loader. |
Headers, duplicates and blanks
JSON object keys must be unique, and CSV headers frequently are not. A spreadsheet
with two columns called notes would lose one of them entirely — the
second silently overwrites the first. We rename collisions to
notes and notes_2 so both survive. Empty header cells become
column_3, numbered by position, rather than an empty key that is awkward
to address in code.
If your file has no header row at all, uncheck the header option and every column is
named column_1 through column_n by position.
Encoding and the invisible Excel character
Excel writes a byte order mark at the start of UTF-8 CSV files. It is
invisible in every editor, but it becomes part of the first header cell — which is why
people end up with a JSON key that looks like name, refuses to match
"name" in code, and drives an afternoon of debugging. We strip it
automatically. Files are read as UTF-8, which covers accented characters, cedillas and
the rest.
Ragged rows
When a row has more values than the header has columns, something is wrong with the
file — usually an unescaped delimiter. The tempting fix is to drop the extras. We keep
them as _1, _2 and show a warning naming the row, because a
conversion that silently discards data is worse than one that produces an awkward key.
Rows with fewer values get null for the missing fields.
Privacy
Every step happens in this tab. The file never leaves your device, which matters more than it sounds: CSV exports are overwhelmingly customer lists, transaction histories, payroll and CRM dumps. Pasting those into a server-side converter means handing a copy of your customer data to a third party you have never audited. Here there is nothing to audit, because there is no upload — you can verify it by opening your browser's network tab, or by disconnecting entirely and converting anyway.
Frequently asked questions
Is my file uploaded anywhere?
No. The parser runs in your browser as plain JavaScript. Nothing is sent to a server, nothing is logged, and there is no account. You can disconnect from the network and the tool still works, which makes it safe for customer exports, payroll files and anything under NDA.
Why did my phone numbers and ZIP codes lose their leading zeros?
They would have, if we converted everything that looks numeric. We do not. Type inference only converts values that survive a round trip: 42 becomes a number because String(42) is exactly "42", but 007 stays a string because String(7) is "7", not "007". Phone numbers, ZIP codes, product SKUs and account numbers keep their exact original text. If you want everything as strings anyway, uncheck the inference option.
My CSV has commas inside a field. Will it break?
No. Any field wrapped in double quotes can contain commas, semicolons, line breaks and quotes. A quote inside a quoted field is written as two quotes in a row, which is the RFC 4180 rule, and the parser handles it. This is exactly where split-on-comma converters produce silent garbage.
What happens if a row has more values than the header?
Nothing is dropped. Extra values are kept as _1, _2 and so on, and a warning tells you which row was ragged. Silently discarding them is worse than an ugly key, because you would never learn that data went missing.
How large a file can it handle?
The limit is your tab's memory, not an artificial cap. Files in the tens of megabytes parse in a second or two on a normal laptop. Very large files are better handled by a streaming tool, because everything here is held in memory at once.