Rename columns under a stated convention, with two that would end up with the same name numbered rather than left to collide
Initializing in your browser…
Keep and reorder chosen columns, with the list built from the widest row so a field beyond the header is not dropped
View, sort and edit a CSV, with an undo stack that goes back exactly one step and a parse report that tells a broken quote from a ragged row
Sort by any number of columns, with the comparison stated: numbers by value, dates by day, text by a collation you choose
A header holding "First Name", "first_name", "Price ($)", "HTTPStatus" and "---", put into snake_case. Two of those five want the same name, one holds characters snake_case has no place for, one is a run of capitals, and one leaves nothing behind at all.
The header, and snake_case
First Name , first_name , Price ($) , HTTPStatus , ---
The plan
first_name applied snake_case; removed " "
first_name (2) numbered, because column 1 already takes the name "first_name"
price removed " ", "(", "$", ")"
http_status a run of capitals followed by a capital and a lower case letter
column_5 named, because there was nothing left
1 name would have been the same as an earlier column's and was numbered.
A CSV header holding the same name twice is legal and unusable.The previous version produced ["first_name", "first_name", "price_($)", "httpstatus", ""] and said nothing about any of it. Two identical names in a header is a file where anything reading a column by name gets one of the two and cannot say which; price_($) is not snake_case, which holds letters, digits and underscores; httpstatus is one word only if the boundary rule is a lower case letter followed by an upper case one, which misses every acronym; and a column with an empty name cannot be referred to at all. Each of the five is now either fixed or reported with which column it happened to.
Apply a naming convention across a whole header, add a prefix or suffix, expand short forms, or find and replace inside the names. Two columns that would end up with the same name are numbered and reported, because a CSV header holding the same name twice is legal and unusable.
Only the header row is rewritten. Every data row is kept exactly as it was, which is checked by reading the download back with Python's csv module and comparing the data rows against the source's. Measured on 2026-09-02 against a second statement of the same conventions written in Python: 216 of 216 conversions correct through the browser, 985 module assertions, 468 corpus assertions and 75 browser assertions.
The baseline was 95 of 135. **A convention was approximated rather than enforced.** The tokenizer was `str.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/[-_]/g, ' ')`, which leaves every character that is not a dash or an underscore exactly where it was: `First Name (US)` came out of snake_case as `first_name_(us)`, `Price ($)` as `price_($)` and `holds, a comma` as `holds,_a_comma`. A snake_case name holds letters, digits and underscores and nothing else, so 20 of 135 results were not in the convention they claimed. Words are now split at anything that is not a letter, digit or mark, and the removed characters are listed rather than left in the name.
**A run of capitals was one word.** `HTTPStatus` became `httpstatus` and `XMLHttpRequest` became `xmlhttp_request`, because the only boundary rule was a lower-case letter followed by an upper-case one. There is now a rule for the last capital of a run followed by a lower-case letter, so those are `http_status` and `xml_http_request`, and one for a digit followed by a capital, so `col2Name` is `col2_name`. All five segmentation rules are written out on the page and in the module.
**Two columns could end up with the same name and nothing said so.** `First Name` and `first_name` both become `first_name` under snake_case; the tool wrote that header and downloaded it. Anything reading such a file by name gets one of the two columns and cannot say which. Every plan is now made over the whole header at once, a repeat gets a numbered suffix, and the collisions are listed above the column list with which column took the name. Measured: all five collision cases in the corpus produced a duplicate header before and none does now.
**A header that the convention emptied stayed empty.** `---`, `???` and a header of spaces all leave nothing behind, and a column with no name cannot be referred to at all; those get `column_3` and the invention is reported. (The shipped rule was inconsistent about it too: `---` emptied and `???` did not.)
**The preview and the download could disagree.** Apply Renames wrote `renames.map(r => r.newName)`, the name BEFORE the convention, the prefix and the find-and-replace had run, so choosing snake_case and pressing Apply wrote the original names unless you separately pressed Apply Bulk Transformations first. What the preview shows is what is written now.
Find and replace inside a name is fixed the same way csv-search-replace was: the replacement is inserted exactly as typed, so a replacement holding `$&` means those two characters; a pattern that cannot be compiled is reported with the engine's own message instead of silently leaving the name alone; and a pattern that can match the empty string terminates instead of looping.
Abbreviation expansion no longer changes the case of the words around it. It used to lower-case the whole name and rejoin with underscores, so expanding a short form imposed snake_case even when no convention was selected. It expands word by word now and leaves the casing to the convention.
Three more: eight conventions rather than five, with Title Case, lower case and dot.case added and every one carrying the definition it enforces; the column count comes from the widest row, so a file whose rows are wider than its header says so; and the download is written by RFC 4180 with a UTF-8 byte order mark, so a header holding an accent or a non-Latin script survives the trip into a spreadsheet. A single-column CSV loads, where it was refused before.
Pick snake_case with abbreviation expansion. fname becomes first_name and ph_num becomes phone_number. If two of your columns would end up with the same name, the second is numbered and the page says which column took it.
Choose camelCase. customer_id and email_addr become customerId and emailAddr, and HTTPStatus becomes httpStatus rather than httpstatus.
Use find and replace with col_ and an empty replacement to drop a repeated prefix across all headers at once. The replacement is literal, so a prefix holding a dollar sign is safe.
No. Only the header row is replaced. Every data row is kept exactly as it was, and that is checked by reading the download back with a separate CSV implementation and comparing the rows to the ones that went in.
The second gets a numbered suffix and the page lists which column took the name. That case used to produce a CSV whose header held the same name twice, which is legal and unusable: anything reading it by name gets one of the two columns and cannot say which. First Name and first_name both become first_name under snake_case, so it is not a rare case.
Five rules, all stated on the page. Anything that is not a letter, digit or mark is a separator and is removed. A lower-case letter followed by an upper-case one is a boundary. In a run of capitals the boundary falls before the last one when a lower-case letter follows, so HTTPStatus is HTTP and Status. A digit followed by a capital is a boundary, so col2Name is col2 and Name. A letter next to a digit otherwise is not, so address1 stays one word.
Because the convention has no place for them. snake_case holds letters, digits and underscores; a parenthesis or a dollar sign is not one of those. Whatever was removed is listed under the convention so you can see it happened. The previous version left them in, which produced names like first_name_(us) that are not snake_case at all.
Yes. Pick a convention and it applies to the whole header, or turn on Selective Mode to transform only the columns you check. Either way the collision check runs over the whole header, including the columns you did not touch.
Yes, with a toggle. The replacement is inserted exactly as typed in both modes, so $& and $1 are those characters rather than substitution patterns; a pattern the engine cannot compile is reported with its own message rather than silently doing nothing; and a pattern that matches the empty string terminates instead of hanging the page.
It is given a name of the form column_3 and the page says why. A column with no name cannot be referred to by anything reading the file.
Rows and columns are parsed and transformed in memory in your browser. No record ever reaches a server.