Most CSV diff tools stop at "here are the rows that changed". That covers maybe a third of what you actually want when you open two files side by side. The other two-thirds — focusing on the columns that matter, matching rows by ID instead of position, picking which side wins for a merged export, getting a plain-English summary of what changed — usually require a script, a spreadsheet wizard, or a paid SaaS. csvdiff.app pulls all of it into one browser tab, and over the last few releases it has grown a second mode: drop a single file and you get a fast viewer with the cleanup tools an Excel user expects (color rules, fill blanks, trim & case, inline edit, column formats). This post walks through every feature in the order you tend to reach for them.
Row-level Diff with Status Pills
Drop two files in and the first thing you see is a unified diff table. Each row is tagged Modified, Added, Removed, or Unchanged, and changed cells highlight the old value next to the new value inline. No squinting between two windows.
| Status | id | name | role | status |
|---|---|---|---|---|
| Modified | 102 | Sara Ahmed | Engineer→Senior Engineer | active |
| Added | 201 | David Miller | Designer | active |
| Removed | 088 | Layla Hassan | Engineer | inactive |
Match Keys — Diff by ID, Not Row Position
Two exports of the same dataset will almost never come back in the same order. A positional diff treats every reordered row as a change, which is noise. Pick a match key column (id, email, sku, order_number) and csvdiff.app pairs rows across files by that key first, then computes cell-level diffs only on the genuine differences. Same export reordered? Zero changes. One row deleted, one added, three modified? You see exactly that, no false positives.
A good match key is unique and stable across both files. If your data has no single-column key, you can still diff positionally — but the match key is what makes the diff trustworthy on real-world exports.
AI Match Key Suggestion
When a file has thirty columns and three of them look key-ish, picking the right match key is half the battle. The AI Suggest button next to the match-key picker hands every column name and a sample of values to your configured LLM and gets back a ranked list — id at the top, then email, then sku — each with a confidence score and a one-line reason. Click pick on a row and the diff recomputes against that key. Your API key stays local; only column names and a few sample values are sent.
Column-Level Filters — Hide What You Don't Care About
A wide CSV is harder to read than a tall one. The Columns menu lets you toggle individual columns on and off, with a search box for files that have dozens of headers. There's also an "Only changed" switch that hides every column where the two files agree on every row — leaving just the columns where something actually moved. On a 40-column export with three differing columns, this turns 40 columns of scrolling into 3 columns of focus.
- idall equal
- nameall equal
- role
- salary
- departmentall equal
- managerall equal
- status
- created_atall equal
- updated_atall equal
| role | salary | status |
|---|---|---|
Sort, Pin, and Filter by Column
Every column header is a control surface. Click the header text to sort that column ascending, click again for descending, click a third time to clear. Click the pin icon to lock a column to the left edge of the scroll area — useful when you have an ID or name column you always want visible while you scroll horizontally through 30 other fields. And click the filter icon to open a value picker: a checklist of every distinct value in that column, with counts. Tick the values you want to keep and the table narrows to just rows where that column matches. Sort, pin, and value filters all stack — sort by salary descending, pin "name", filter "department" to just Engineering and Design, and you have a focused view in four clicks.
id | name | department⚲ 2 | salary▼ | status |
|---|---|---|---|---|
Pinned columns stay anchored to the left while everything else scrolls. Combined with column visibility and value filters, you can shape a 50-column export into a 5-column working view without touching a spreadsheet.
Bulk Find-and-Replace from the Column Filter
Open any column's value filter and the same panel that lists distinct values doubles as a bulk-replace surface. Tick the values you want to rewrite, type the replacement, hit apply, and every matching cell in that column updates in place — across thousands of rows in a single click. The rewritten rows flash so you can visually verify the scope of the change, and the edits are non-destructive (export to commit them, or clear to revert).
| # | role |
|---|---|
| 1 | engineer→Software Engineer |
| 2 | senior engineer→Software Engineer |
| 3 | engineer→Software Engineer |
Status Filters — Modified, Added, Removed
Above the table, six pills (Changes only, All rows, Unchanged, Modified, Added, Removed) act as a single-click filter on row status. "Changes only" is the default and usually what you want — it hides every Unchanged row so the table is just the deltas. Toggle Modified to audit edits, Added to review new records, Removed to confirm deletions.
One click to slice the diff. Filters compose with column visibility and search.
Search — Cross-Cell, Across Both Files
Press / and a search input takes focus. The query runs across every visible cell in both files at once, including row numbers — useful for tracking down a specific record by name, ID, or any value. The match count updates as you type, and the search composes with the column visibility and status filters so you can narrow first and search within the narrowed set.
| id | name | role | department |
|---|---|---|---|
| 102 | Sara Ahmed | Senior Engineer | Platform |
| 145 | Sara Khan | Designer | Brand |
| 188 | Sarah Lee | Data Analyst | Insights |
View Modes — Diff, File A, File B
Sometimes a diff isn't what you need. The view switcher flips the same table between three modes: Diff (the unified comparison), File A (just file A as a viewer), and File B (just file B as a viewer). Same column visibility, same search, same sort — you don't lose your place when you switch contexts.
Single-File Viewer Mode
Diffing needs two files; viewing only needs one. Drop a single CSV (or JSON) and csvdiff.app skips the diff machinery entirely and opens the file in a clean viewer: paginated, virtually scrolled, with the same search, sort, pin, column visibility, and value filters you get in diff mode. The viewer is what you use when somebody hands you a 200k-row export and asks "what's in it?". Open in three seconds, scroll without lag, find what you need, close the tab.
| order_id | customer | sku | total |
|---|---|---|---|
| O-10242 | Ayesha Khan | SKU-882 | $240.00 |
| O-10243 | Sara Ahmed | SKU-104 | $98.50 |
| O-10244 | John Smith | SKU-882 | $240.00 |
| O-10245 | Zain Ali | SKU-451 | $1,205.00 |
Color Rules Per Column
Every column header in the viewer (and in the diff table when you switch to File A or File B mode) has a palette menu. Pick a rule and the cells in that column get a subtle tint that makes patterns obvious without changing the underlying data. Five rules ship today — heatmap (gradient by numeric value), category (a stable color per unique value), above/below average (green/red against the column mean), empty cells (amber on blanks), and duplicates (highlight values that appear more than once).
| name | salary | status |
|---|---|---|
| Ayesha | $120,000 | active |
| Sara | $98,000 | on leave |
| John | $155,000 | active |
| Zain | $87,000 | pending |
| Maya | $142,000 | active |
Fill Blanks From Above
Pivot-style exports leave grouping columns blank under their first row, because Excel renders them that way visually but the CSV inherits the blanks. The column menu has a one-click "fill blanks from above" toggle that copies the last non-empty value down into every blank below it, stopping at the next populated cell. The filled cells highlight green so the change is visible, and toggling the option off restores the original blanks without touching any manual edits you made in between.
| region | city | revenue |
|---|---|---|
| West | San Francisco | $240k |
| empty | Seattle | $180k |
| empty | Portland | $92k |
| East | New York | $310k |
| empty | Boston | $145k |
| region | city | revenue |
|---|---|---|
| West | San Francisco | $240k |
| West | Seattle | $180k |
| West | Portland | $92k |
| East | New York | $310k |
| East | Boston | $145k |
Text Format — Trim & Case
Stray whitespace and inconsistent casing are the two cheapest data-quality bugs to fix, and the two most tedious to fix by hand. The Text format menu on any column gives you a trim toggle (strip leading, trailing, and collapse internal whitespace) and a four-way case switch (None, lower, UPPER, Title). Changes are display-only until you export — the source CSV stays untouched in memory.
Drag-and-Drop Column Reorder
Every column header has a small drag handle on the left that appears when you hover. Grab it and the column lifts out of the row as a floating card that follows your cursor, while the original slot stays in place as a faded ghost. Move over any other column and that column lights up indigo — release to swap the two. It feels closer to Linear or Jira than to a spreadsheet, and it avoids the "scroll the page accidentally while reordering" trap of old drag implementations.
A few details that matter: the swap respects pinned-first ordering, so pinned columns only swap with other pinned columns and unpinned with unpinned — you never lose a pin by reordering. Reorder works in both modes, so the same gesture cleans up a diff view or a viewer view. And like every other state in csvdiff.app, the order lives in memory only; refreshing the tab starts you fresh, and your underlying CSV file never changes until you hit export.
id | name | region | status | amount |
|---|---|---|---|---|
Tip: combine drag-reorder with pin. Pin the two or three columns you actually care about, then drag-reorder them into your preferred sequence. Wide files become readable without filtering anything out.
Inline Edit and Header Rename
Click any cell to fix a typo in place. Click any header to rename the column. Both edits are non-destructive — they live in memory until you export, so a misclick is one Cmd-Z away from the original. For changes that span many rows, the bulk find-and-replace from the column filter (covered above) is the faster path; inline edit is for the one-off fixes that always come up when you scroll the file.
Column Formats — Currency, Dates, Numbers
A column of numbers can mean a dozen different things: dollars, percentages, durations, IDs, raw integers. The Format menu on every column lets you pick how the values render: currency (USD, EUR, GBP, and more), percent, number with separators, date and datetime (ISO, US, EU, relative), or scientific notation. The viewer detects a likely format from the column values and suggests one; you can override with one click, and AI Suggest will pick a format with reasoning if the data is ambiguous. Like the text format, this is display-only — your CSV bytes don't change until you export with "apply formats" turned on.
Smarter Numeric Sort
Sorting a column of "$1,200", "$98", "$10,000" alphabetically puts $10,000 between $1,200 and $98 — useless. The sort now detects numeric-looking values and sorts by their parsed magnitude, ignoring currency symbols, thousands separators, and stray whitespace. Same for percent columns and dates. The mixed case (a column of numbers with the occasional "N/A") still sorts numerically, with the non-numeric values grouped at the end.
- $$1,205
- $$240
- $$10,200
- $$98
- $$3,450
- $$10,200MIN
- $$3,450
- $$1,205
- $$240
- $$98MAX
Resolve and Merge — Pick a Winner per Cell
Diffing is half the job; the other half is producing a single merged file. On every changed cell, click A or B to pick which side wins for the export. Row-level controls let you keep or drop entire Added/Removed rows. Bulk actions handle the common cases — "keep all from A", "keep all from B", "accept everything as new". The export honors every choice you made.
| ID | role | salary | status |
|---|---|---|---|
| 102 | AEngineerBSenior Engineer | A95,000B110,000 | active |
| 145 | Designer | 78,000 | AactiveBarchived |
Export — CSV or JSON
When you're happy with the resolutions, the footer has Download CSV and Download JSON buttons. The export uses your current filter and column visibility, so you can ship either the full merged dataset or just the changes-only slice. Everything is generated in the browser — the file goes straight from memory to your disk.
84 rows · 9 columns · generated client-side
AI Summary — Plain-English Diff
For files where you need to communicate what changed (a stakeholder, a PR description, a release note), the AI Summary popover ships the diff to your configured LLM and gets back a structured paragraph: counts by status, a few representative examples, and patterns it noticed (e.g. "all status changes were active → archived"). You bring your own API key — it never touches our server.
- Six employees received role and salary updates, all in the Platform department.
- Four new hires were added across Engineering and Design.
- Two records were removed — both had status set to "inactive" in file A.
Privacy — 100% Client-Side
Every feature above runs in your browser. The CSV parser, the diff algorithm, the merge logic, the export — none of it makes a network request with your data. The only optional exception is the AI summary, which uses a key you supply and goes directly to your chosen provider. For regulated data (PII, finance, health), this is usually the deciding factor.
Putting It All Together
A typical diff session: drop two CSVs, pick a match key (or accept the AI suggestion), switch to "Only changed columns", filter to Modified rows, search for the customer ID a teammate flagged, click B for the cells where the new values are correct, hit Download CSV. Three minutes, no scripts, no spreadsheet macros, no data leaving the tab.
A typical viewer session: drop one CSV, apply a heatmap to the salary column, run a duplicates check on email to catch double-imported rows, fill blanks down the region column so the file groups cleanly, trim and title-case the name column, format revenue as USD currency, and export the cleaned file. Same tab, same privacy guarantees, no spreadsheet — and no version of you wishing the CSV had come out cleaner from the source system.
That's the tool: built for the things you actually do with CSVs, not the things the demo shows.