Every project drifts. A config that worked last week, an environment file copied between machines, a generated client that should be identical, or a log snapshot taken before and after a deploy all require you to find what actually changed. Reading two files side by side is slow and error-prone. A diff turns the hunt into a clear verdict: same, added, removed, or edited, line by line.
The file comparison tool on this page is built for that exact verdict. It is not a merge tool and it does not guess intent. It aligns two plain-text inputs, highlights every difference, and lets you walk through the changes in order. There is no upload, no account, and no backend required.
Compare .env, nginx.conf, docker-compose.yml, or Kubernetes manifests between commits or hosts to catch the one line whose value shifted.
Diff staging vs production variable dumps or feature-flag files. Spot a missing key or a flipped boolean before it becomes an incident.
Compare generator output like OpenAPI clients, Protobuf stubs, or migration SQL before committing. The diff shows only what the generator actually changed without formatting noise.
Drop two log excerpts, CSV exports, or NDJSON slices side by side. Added and removed lines surface regressions, missing events, or pipeline gaps instantly.
The editor accepts text instead of binaries, and it offers three paths that all end in the same synchronized view. Pick the method that matches where your content already lives.
| Method | When to use it | What happens |
|---|---|---|
| Paste | A snippet is already on your clipboard, such as a code block from a PR, a log excerpt, or a config copied from SSH. | Click Paste on that side or press Ctrl+V / Cmd+V. The editor on that side is replaced with the clipboard text and the diff recomputes immediately. |
| Upload | The content lives on disk, like a .txt, .json, .yml, .xml, or any text-based source file. | Drag the file onto the dropzone or click it to open the file picker. Only one file per side is accepted. Binaries such as .pdf, .png, .zip, or .exe are rejected with a toast message. Files larger than 10MB are attached as a File reference and streamed chunk by chunk instead of being read into memory all at once. |
| Tool handoff | You just decoded two Base64 payloads in separate tabs and want to know why they differ, or a formatter output is ready to compare. | On the Base64 page, every decode opens a numbered tab. Hover any tab and click the ⇄ compare icon — the two decoded texts land here as Left and Right, with the tab labels as file names. The same handoff works from JSON Compare: navigate here and the shared store already holds the content. |
The result is a synchronized side-by-side view: two panes scroll in lockstep so every change on one side is aligned with its counterpart on the other. The annotated mock below shows the real layout:
+, −, or ~ for add, remove, or change.+ marker. In the example, PORT=8080 appears only on the right, as the left line is absent.− and a strikethrough. Here PORT=3000 was removed in production, so you see exactly where.~ marker and the differing span is highlighted inline. TIMEOUT=30 → TIMEOUT=60 is a single edit, not an add plus a remove.No matches or 2 of 7), up/down match navigation, Copy (always), Paste (disabled only in read-only >10MB mode), and Trim (file-mode only, which replaces Format/Minify). The far-right controls walk the global diff hunks, and Exit clears the store to return to the formatter.Files larger than 10MB do not fit comfortably in a React string prop. The editor switches to a streaming path the moment either side exceeds the threshold, whether the content came from a paste or a dropped File object.
Large file detected. Optimized Read-Only mode enabled for performance. History (Undo/Redo) and Folding are disabled. Showing chunked data as it loads.
readOnly and domReadOnly are true on both panesThe 10 MB threshold applies per side. Below it, your content is preserved automatically if you reload the tab. Above it, the file is streamed directly into the viewer without being stored, so a reload starts fresh. Clearing both sides returns the editor to full interactive mode.
Everything on this page runs in your browser. There is no upload, no server-side processing, and no storage beyond what is needed to display the current comparison.
That also means limits are local and not enforced by a quota:
Text never leaves the device. Close the tab and the content is gone; reload and the store is empty.
Tens of megabytes are comfortable on desktop. The practical ceiling is the tab's available memory, and very large single-line files are the hardest to render.
Files are read locally from your device. The network is only used if you explicitly import content via a URL.
These are the cases that surprise people who compare files for the first time. Each one is real behavior, not a bug, and has a simple fix.
ignoreTrimWhitespace is false, so the diff treats indentation and trailing spaces as meaningful. That is deliberate because for plain text, a space is data. In JSON mode, the same flag is true. If the noise bothers you, press Trim: it trims each line, drops empty lines entirely, and collapses runs of two or more spaces into one. Use it when you care about content, not formatting.CRLF (\r\n), while macOS/Linux and most git checkouts use LF (\n). Trim handles this too: it splits on \n and trim() strips the stray \r, normalizing line endings to LF. If you need to preserve line ending style, don't trim; instead configure your editor or git config core.autocrlf before comparing. or mojibake▾availableModes: ['code'] and capabilities canCopy / canPaste / canDownload only. No canFormat, no canMinify, no Tree or Table view. The page is a plain-text diff. For structured data with formatting, use JSON Compare or the JSON Formatter.No. Pasting, dropping, URL import, and the diff itself all happen in your browser. Nothing is sent to a server and nothing is persisted beyond the current tab's in-memory store. Clearing both sides or leaving the page calls reset() and empties the store.
Any text-based file such as .txt, .env, .json, .yml, .xml, .csv, .md, or source code. Binaries such as .pdf, .docx, .png, .zip, or .exe are blocked by extension. Files like image/*, video/*, and audio/* are blocked by MIME type before anything is read.
Trim is not lossless. It splits on newlines, trims each line, drops any line that becomes empty, joins with \n, and then collapses every run of two or more whitespace characters into a single space. Blank lines you intentionally left will be deleted. The editor is updated in place and the transformed text is what gets diffed and copied.
There is no hard file-size cap, but the >10MB path is read-only and the tab's memory is the real limit. Many users comfortably compare 20–30MB text files on desktop, but beyond that the browser may slow or run out of memory. If you only need a section, split the file first.
Yes. On the Base64 page, each decode opens a numbered tab. Hover a tab and click the ⇄ icon, then do the same on a second tab. Both decoded texts arrive here side by side, with the tab labels shown as file names. You can also open JSON Compare and navigate to this page — the content carries over automatically.
Use JSON Compare when you need JSON-aware alignment and formatting; it tolerates whitespace noise by default. Use folder compare to map entire directory trees. Use this file compare when the input is plain text, when whitespace matters, or when you want the strictest line-by-line verdict.
Once the file diff is clean, hand structured payloads to the JSON Formatter for validation and formatting, switch to JSON Compare when the content is JSON and you want lenient whitespace handling, use folder compare to spot missing or renamed files across two directories, or decode an embedded payload with the Base64 tool and hand the decoded tabs back here.
Paste or drop two text files (up to 30MB side-by-side) to see every added, removed, or changed line in a synchronized and searchable diff. All processing happens locally; nothing is uploaded.