JS Minifier
Minify JavaScript with Terser and read an honest size report — original, minified and gzip bytes — entirely on this device.
Whitespace and plain comments go; global names and console calls stay.
Removes dead code and folds constant expressions; console calls are kept. Shortens local variable names — global and top-level names are left unchanged. Off removes every comment, including /*! license banners.
Size report
- Original
- —
- Minified
- —
- Gzip
- —
- Saved
- —
Press ? for shortcuts
Do it offline:$ npx terser input.js --compress --mangle --output output.min.js
How to use it
- Paste JavaScript or drop a .js file up to 5 MB — small scripts minify as you type, larger ones on a background thread.
- Toggle Compress, Mangle and Keep /*! license comments; each change re-runs the current script.
- Read the report card — original, minified and gzip bytes, and percentage saved — or a plain already-minified line.
- Copy the output, download a .js file, or share a link that carries only your code and options.
Paste a script and the minified version appears as you type, beside a report card that names the numbers a bundle cares about: original bytes, minified bytes, gzip weight and percentage saved. Terser does the work — renaming locals, folding constants and dropping dead code — while /*! license banners stay by default, and nothing you paste is uploaded.
Examples
function formatMoney(amount, currency) {
const symbols = { USD: "$", EUR: "€", GBP: "£" };
const symbol = symbols[currency] || currency;
return symbol + amount.toFixed(2);
}
function applyDiscount(price, percent) {
const saved = price * (percent / 100);
return formatMoney(price - saved, "USD");
}
function formatMoney(n,o){return({USD:"$",EUR:"€",GBP:"£"}[o]||o)+n.toFixed(2)}function applyDiscount(n,o){return formatMoney(n-n*(o/100),"USD")}Two functions: the card reads 312 B → 148 B · saved 53%, and the result is 141 B after gzip (a Node zlib figure; a browser CompressionStream may differ slightly). The locals become single letters, yet the top-level names formatMoney and applyDiscount survive, so the call between them resolves.
/*! shim v2.1.0 | (c) 2026 Example Inc | MIT */
function polyfillClosest(el, selector) {
while (el) {
if (el.matches(selector)) return el;
el = el.parentElement;
}
return null;
}
/*! shim v2.1.0 | (c) 2026 Example Inc | MIT */
function polyfillClosest(e,l){for(;e;){if(e.matches(l))return e;e=e.parentElement}return null}A /*! banner is the license convention, so it rides through by default while the code around it compresses — 193 B → 142 B · saved 26%. Set comments to Strip all and it goes too, down to 94 B.
function isEven(n){return n%2==0}function isEven(n){return n%2==0}Hand it code Terser cannot shrink further and it says so: input and output match at 33 bytes, so the card reads Already minified — saved 0 bytes, not a hollow gain.
function greet(name) {
return "Hello, + name;
}
JavaScript could not be parsed: Unterminated string constant (line 2, column 10).A missing closing quote lets the string run to the end of the line, so nothing is emitted and the input stays put. The wording is Terser's own — “JavaScript could not be parsed: Unterminated string constant (line 2, column 10).” Jump to error lands the caret on the opening quote.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Mangling only touches local names
Terser shortens variables, parameters and inner functions but leaves top-level and global names alone — renaming an export would break every caller. Above, applyDiscount still calls formatMoney by name. Aggressive top-level mangling stays off.
Compress folds constants and drops dead code — console stays
The compress pass folds 1 + 2 to 3, deletes branches guarded by a constant false, and drops bindings nothing reads. It leaves console and debugger calls alone — silently losing your logging on a minify would be a nasty surprise.
Top-level await is the one module-only stopper
This tool minifies scripts. Most module-shaped code still minifies — import.meta, a dynamic import(), an export line — but a top-level await means nothing in a script, so Terser rejects it as a parse error. Wrap it in an async function.
Gzip is measured in your browser, not here
The Gzip cell comes from your browser's CompressionStream; the figures on this page come from Node's zlib. Compressors and levels differ, so read any gzip number as an estimate. On a tiny snippet gzip's header can push it above the minified size — the real wins arrive at module scale.
Minifying is not obfuscating
A minifier optimizes for size and keeps the program working the same, readable again through a source map. An obfuscator does the opposite — it rewrites logic to resist reading and usually grows the file. This page only makes code smaller.
Already minified, or barely smaller, is said out loud
When output equals input byte for byte, the status is Already minified — saved 0 bytes, never a fabricated percentage. Under a two-percent gain a line says the file is essentially minified already, so a small number never reads as a failure.
Common errors
JavaScript could not be parsed: Unexpected token: punc (}) (line 4, column 1).- A property access with nothing after the dot leaves the parser expecting a name; the closing brace is where it gives up. Jump to error lands on the position Terser reports, in Terser's own words.
This tool minifies up to 5MB of JavaScript; beyond that, try the CLI: npx terser input.js --compress --mangle --output output.min.js- Rather than minify part of an oversized paste, the page declines it and leaves your code in the editor. The command runs the same Terser on your machine, with nothing uploaded.
JavaScript minification failed: RangeError: Maximum call stack size exceeded- The catch-all for when Terser throws instead of reporting a parse position — reproduced with hundreds of thousands of nested parentheses. Nothing is emitted; the input is untouched.
FAQ
Does minifying change how my JavaScript behaves?
In the default modes, no. Terser rewrites the source — shorter local names, folded constants, dead branches gone — without changing what runs. The one rule: mangling renames locals only, so anything another file imports by name keeps resolving.
Should I still gzip after minifying?
Yes — they do different jobs, and the card shows both. Minifying removes characters a JavaScript engine never needs; gzip then encodes what is left more tightly for transport. On the first example the gzip figure sits below the minified byte count — the pairing most builds ship.
Is minifying the same as obfuscating?
No. Minifying is a size optimization you can reverse with a source map; obfuscation deliberately makes code hard to follow and tends to grow the file. This page only minifies — it never sets out to hide what your code does.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Re-run the minifier on the current script.
- ⌘/Ctrl + S
- Save output as a .js file — this overrides the browser's save-page shortcut.
- ⌘/Ctrl + C
- Copy the whole output when nothing is selected; a real selection copies itself.
- ⌘/Ctrl + Shift + C
- Force a copy of the whole output, selection or not.
- t
- Toggle the color theme — idle while typing or composing with an IME.
- ? or ⌘/Ctrl + /
- Open help — the dialog lists every binding and where a browser might grab it.
- Esc
- Close the top dialog; it never clears your input.