JavaScript Minifier_

Minified through a real parser rather than a pattern match, because JavaScript cannot be compacted by looking at characters: a /b/ c is a division or a regular expression depending on what came before it, and a line break can insert a semicolon that changes what a function returns.

The three operations are separate switches, because they carry different risks. Removing whitespace is always safe. Renaming your variables is safe unless your code reads its own names. Rewriting expressions is safe unless the compressor cannot see a side effect. When somebody says minification broke their build, it is almost always the second or the third — and the panel above names which of your patterns are exposed.

toolkit.codes/js-minifier
Before
After
Removed
Smaller by
UTF-8
Ready
100% LOCAL
Input
JavaScript or an ES module, from a snippet to a few megabytes. Modern syntax — classes, private fields, optional chaining, top-level await — is parsed rather than tolerated.
Output
Minified source with licence comments preserved. What comes out is what a bundler would produce with the same switches, because it is the same tool.
Processing
Parsed and re-emitted in this tab by terser, the minifier behind most JavaScript build pipelines. Source you paste is often unreleased, and there is nowhere for it to go from here.
Limits
No bundling, no tree-shaking across files, no source map. This minifies one file; a build does the rest, and a build is where this belongs when it can be.
The switch that breaks frameworks
Mangling renames every local and parameter. Anything that reads those names at runtime stops working: dependency injection matched on parameter names, a registry keyed on constructor.name, code assembled as a string and passed to eval. None of it errors at minify time — it errors later, in production, in a way that does not point back here. The risk panel flags all three when it sees them.

Three operations, three different risks

Whitespace: always safe, and rarely the win

Removing line breaks and indentation cannot change what a program does, provided the tool knows where a break is load-bearing. That proviso is the whole reason a parser is needed: JavaScript inserts semicolons at line ends under rules complicated enough that a return followed by a newline returns undefined regardless of what is on the next line. A parser knows; a regular expression guesses, and the code it produces still looks like code.

Mangling: safe unless your code reads its own names

Renaming locals to single letters is where most of the saving comes from and where nearly all the breakage does. The pattern is always the same: something at runtime looks up an identifier by its text. Angular's original injector matched service names to parameter names. Serialisers and plugin registries key on constructor.name. Code built as a string and handed to eval refers to variables that no longer exist under that spelling. None of these fail at build time, which is why they are found in production.

Compressing: safe unless a side effect is invisible

Compression folds constants, removes unreachable branches, and deletes values nothing uses. That last one is the sharp edge: an assignment whose result is never read looks like dead code, and if the expression producing it had a side effect the compressor could not see — a getter that logs, a proxy that counts accesses — the effect goes with it. It is also why an unused top-level constant vanishes entirely, which surprises people testing a minifier with a one-line file.

Top-level mangling, and when it is wrong

By default the outermost names survive, because in a plain script they are globals other files may reference. Turn top-level mangling on and function initialise() becomes function n() — correct inside a module whose exports are explicit, and quietly catastrophic in a script another file calls into. If your file ends up in a bundle, the bundler already handles this; if it is loaded directly by a <script> tag, leave it off.

Do this in your build, not in a tab

A page like this is for a file you were handed, a snippet you want to shrink for an inline script, or an experiment with what the switches do. Anything that ships should be minified by the build that also produces its source map — because a production error stack from mangled code without a map is a list of single letters and line one, and the map is the only thing that turns it back into something you can act on.

Paste, then decide how far to go

  1. 01Paste the JavaScript or upload a file. Mangle and Compress are on, which is what a build would do by default.
  2. 02Read the risk panel before shipping the output. It names the patterns in your own code that stop working once names are gone.
  3. 03Turn Mangle off to see how much of the saving was names, and how much was whitespace. On most real code it is mostly names.
  4. 04Leave Top-level off unless the file is a module. In a plain script it renames the globals other files call.

An inline script that has to be small

A snippet going straight into a page template, where there is no build step to run it through. Minifying by hand is exactly what this is for.

Written for reading
function trackClick(element, category) {
  const payload = { c: category };
  navigator.sendBeacon("/t", JSON.stringify(payload));
}
Ready for the template
function trackClick(n,t){navigator.sendBeacon("/t",JSON.stringify({c:t}))}

Injection that stops resolving

The container matches services to parameter names. Mangling renames the parameters, so nothing matches and the error appears far from the cause.

Works unminified
app.controller("Main", function($scope, $http) { … });
Breaks minified — annotate instead
app.controller("Main", ["$scope", "$http",
  function(a, b) { … }]);

A registry keyed on class names

constructor.name returns a single letter after mangling, so lookups miss. The code runs and the registry is simply empty.

Before
registry[Widget.name] = Widget;
// "Widget"
After mangling
registry[n.name] = n;
// "n" — and next build, something else

Measuring what each switch buys

Turning mangling off and on separates the two halves of the saving, which is the fastest way to decide whether the risk is worth the bytes.

Whitespace only
2,480 bytes → 1,910 · 23% off
Plus mangling and compression
2,480 bytes → 968 · 61% off

Which switch caused it

SymptomLikely switchWhat to do
Dependency injection stops resolvingMangleAnnotate dependencies explicitly rather than relying on parameter names.
A registry or serialiser finds nothingMangleStop keying on constructor.name; use an explicit static identifier.
eval can no longer see a variableMangleThe names it refers to are gone. Pass values in rather than naming them in a string.
A global other files call is undefinedTop-levelTurn top-level mangling off, or attach the export to an object deliberately.
A side effect stopped happeningCompressThe value it produced was never read, so the expression looked dead. Assign it somewhere observable.
An unused constant vanishedCompressWorking as designed. Export it or use it if it needs to survive.
console.log output disappearedDrop consoleThat switch is off by default here; a build often has it on.
Output is empty for a one-line fileCompressNothing was exported or used, so the whole file was unreachable.

Every row is a real failure mode of a specific switch rather than of minification in general. That distinction is the fastest way back to a working build, and it is why the switches are separate here.

Minifying without losing an evening

  • Generate a source map in your build. Minified stack traces are unreadable without one, and no amount of care at minify time substitutes for it.
  • Annotate dependency injection explicitly. Any framework that resolves by parameter name has a form that survives mangling, and using it costs nothing.
  • Never key a registry on constructor.name. Add a static identifier the minifier cannot touch, because the name changes with every build.
  • Test the minified bundle, not just the source. Mangling and compression failures are invisible until the code runs, so a smoke test against the built artefact is the only thing that catches them.
  • Leave top-level mangling off for anything loaded by a script tag. The names at the outer level are the interface other files use.

Failures that appear later, not here

Nothing errors at minify time

The output is valid JavaScript in every case above. It parses, it loads, and it does the wrong thing at runtime — which is why the risk panel looks for the patterns rather than waiting for a failure.

A stack trace without a source map is unusable

Every function is a single letter and everything is on line one. Producing the map is part of minifying properly, and a page like this does not produce one.

Compression can delete a side effect

An expression whose value nothing reads looks dead. If it was reaching a getter or a proxy that did real work, that work goes with it.

Minifying already-minified code gains nothing

It is idempotent, so a second pass returns the same output. If you expected another reduction, what you actually want is compression on the wire or fewer dependencies.

Engine, switches and limits

Engine
terser, the minifier behind most JavaScript build pipelines. The source is parsed into a syntax tree and printed back from it, which is the only way to be right about semicolon insertion and regular-expression literals.
Switches
Mangle renames locals; Top-level extends that to the outermost scope; Compress rewrites and removes; Drop console strips console calls. Each maps to the terser option of the same name, so the result matches what your build produces.
Target
ES2020 output. Nothing is transpiled down — a minifier shortens code, it does not change which engines can run it.
Comments
Removed, except those beginning with an exclamation mark, which is the convention for licence text that must survive a build.
Not provided
No bundling, no cross-file tree-shaking, and no source map. The last of those matters most: ship a map from your build or accept unreadable stack traces.
Limits
Input is refused above 4 MB. A file that size belongs in a build rather than a browser tab.
File handling
Uploads are read inside the page with the browser File API and are never transmitted; Download writes out what is already in the tab.
Network
None from tool code. A test sweep calls every function this page uses with fetch and XMLHttpRequest replaced by stubs that throw, so a stray request fails the build instead of shipping. Disconnect from the network and the page still works.

Questions about minifying JavaScript

Why did my code break after minifying?

Almost always mangling or compression rather than whitespace. Mangling renames locals, so anything reading its own identifiers at runtime — injection by parameter name, constructor.name, eval — stops matching. Compression removes expressions whose values nothing reads, which takes any invisible side effect with them. Turn the switches on one at a time and the culprit appears immediately.

Is minified JavaScript slower or faster?

Marginally faster to download and parse, and identical to execute. The engine compiles the same operations either way; shorter names do not make lookups quicker. The gain is transfer size, and even that is smaller than the file-size figure suggests once compression is applied on the wire.

What is the difference between minifying and obfuscating?

Intent, and reversibility of intent. Minifying makes code smaller and readable structure is collateral damage. Obfuscating deliberately makes code hard to understand — string arrays, flattened control flow, encoded identifiers — and usually makes it larger and slower. A minified bundle can be formatted back into something followable; an obfuscated one cannot.

Should I minify in the browser or in my build?

In your build, whenever there is one. A build produces the source map alongside the minified file, and without that map a production stack trace is a list of single letters. A page like this is for a snippet, an inline script, or seeing what the switches actually do.

Does minifying protect my source code?

No. Everything is still there — every string, every URL, every piece of logic — just spelled differently. Formatting it restores readable structure in seconds. If code must stay private, it belongs on a server, not in a browser.

Why is my minified output empty?

Because nothing in the file was reachable. A one-line script declaring an unused constant is entirely dead code with compression on, and the compressor is right to remove it. Export the value or use it, and it survives.

Can I minify TypeScript?

Not directly — the type annotations are not JavaScript and the parser will reject them. Compile to JavaScript first, which every TypeScript build already does, and minify the output.

Why does top-level mangling break my script?

Because the outermost names are the interface. In a plain script loaded by a script tag, other files call those functions by name; renaming them makes the calls fail. Inside a module with explicit exports the outer scope is private, which is why the switch exists and why it is off by default.

Does my code leave the browser?

No. The work is JavaScript running in this tab. Every function it calls is covered by a test that stubs fetch and XMLHttpRequest to throw, so a request that slipped in would break the build rather than reach a server — and you can confirm it for yourself by disconnecting and carrying on.