Local-firstYour text never leaves your browser. No upload, no account, no server.
Most online text tools send whatever you paste to a server, run the work there, and send a result back. Your words sit in someone else's logs, backups and databases, and you have to trust a privacy policy.
Local-first means the opposite: the whole tool is downloaded to your browser and runs on your own device. Nothing you type is transmitted anywhere: not to us, not to anyone. There is no server that could receive it, which is a stronger guarantee than a promise not to look.
Three consequences worth knowing:
Paste CSV and get JSON back: an array of objects keyed by your header row, or an array of arrays if there is no header. Quoted fields containing commas, newlines and escaped quotes are parsed properly rather than split on, which is where most quick converters produce quietly wrong output. Because it runs in your browser, a customer export or a finance report can be converted without uploading it to anyone.
A quoted field containing a comma
name,role Ada,"Engineer, lead"
[
{
"name": "Ada",
"role": "Engineer, lead"
}
]The obvious implementation splits each line on commas, and it is wrong for most files that came out of a spreadsheet. A field wrapped in quotes may contain commas, line breaks, and quote characters escaped by doubling them. Splitting on the delimiter shreds all three.
The failure is quiet, which is what makes it dangerous. A row containing "Smith, John" becomes two columns, every subsequent value shifts left by one, and the output is still valid JSON, just with data in the wrong fields. Nothing errors.
This parses character by character, tracking whether it is inside a quoted field, which is what RFC 4180 actually describes. A field containing a newline stays one field, and a doubled quote becomes a single literal quote.
Values that are genuinely numeric become JSON numbers, and true, false and null become their JSON equivalents. That is usually what you want, because a column of prices arriving as strings has to be converted somewhere.
The conversion is deliberately strict about what counts. A zero-padded identifier like 007, a version string like 1.2.3, and a phone number with a leading plus all stay strings, because turning them into numbers loses information that cannot be recovered. The test is whether the value survives a round trip through a number unchanged; if not, it stays as written.
Real exports have rows with missing trailing cells. Every object gets the full set of keys regardless, with an empty string where the cell was absent, so the result has a consistent shape. Code consuming the JSON can then rely on a key existing rather than checking each one.
A header cell that is blank would produce an empty-string key: legal JSON, and impossible to reference readably. Those are named by position instead, as column2, column3 and so on, so every key is usable.
They stay part of the value. The parser tracks quoting character by character, so a field like "Smith, John" comes through as one value rather than two. The same applies to newlines and to escaped quotes written as doubled quote characters.
Real numbers are. Values that only look numeric are deliberately left as strings: a zero-padded id like 007, a version like 1.2.3, and anything that would change if it made the round trip through a number. Silently turning an identifier into a number is a data-corruption bug that surfaces much later.
Yes. Semicolon-separated files are the norm in locales where the comma is the decimal separator, and tab-separated is what you get pasting straight out of a spreadsheet. Pick the delimiter above the input.
The missing keys are still present with an empty string value, so every object has the same shape. That matters when the result is going somewhere that expects a consistent schema.