JS minifier

Client-side only. Terser runs in this tab from a file on toolpetal.com. Nothing is uploaded or saved.

Tip: Ctrl/Cmd+Enter minifies. The minifier engine (Terser) loads once, the first time you focus the input or press a button.

What each checkbox changes

OptionWhat it doesUntick it when
Compress Rewrites code into shorter equivalents: drops dead branches, folds constants, turns if blocks into && or || expressions, inlines single-use functions. Runs two passes. You want output that still reads like your source, or you are chasing a bug and want to rule the compressor out.
Mangle local names Renames variables and parameters that cannot be seen from outside their function to one or two letters. Property names are never touched. Something reads parameter names at runtime, such as an old dependency injection setup that parses fn.toString().
Keep /*! and @license comments Keeps comments that start with /*! or contain @license or @preserve. Everything else is removed. The code is your own and carries no license text you are required to keep.
ES module Treats the input as a module: strict mode, and top-level names are private to the file, so they can be renamed, inlined or dropped. Leave it off for classic scripts. In a classic script, top-level functions and vars are globals that other scripts may call.

One file, four settings, measured

This is the Load sample input: 763 bytes with a license banner, comments and normal indentation.

/*! cart-total v1.2.0 | MIT */

// Format a number of cents as a price string.
function formatPrice(cents, currency) {
  const amount = cents / 100;
  return new Intl.NumberFormat("en-US", {
    style: "currency",
    currency: currency || "USD"
  }).format(amount);
}

/* Sum line items, skipping anything with a bad quantity. */
function cartTotal(items) {
  let total = 0;
  for (const item of items) {
    if (!Number.isFinite(item.qty) || item.qty <= 0) {
      continue;
    }
    total += item.priceCents * item.qty;
  }
  return total;
}

const items = [
  { sku: "TEA-01", priceCents: 1250, qty: 2 },
  { sku: "MUG-07", priceCents: 1800, qty: 1 },
  { sku: "BAD-00", priceCents: 999, qty: -1 }
];

console.log(`Total: ${formatPrice(cartTotal(items))}`);

With the defaults (Compress, Mangle and Keep license on, ES module off), Terser 5.51.2 returns 451 bytes:

/*! cart-total v1.2.0 | MIT */
function formatPrice(t,e){const r=t/100;return new Intl.NumberFormat("en-US",{style:"currency",currency:e||"USD"}).format(r)}function cartTotal(t){let e=0;for(const r of t)!Number.isFinite(r.qty)||r.qty<=0||(e+=r.priceCents*r.qty);return e}const items=[{sku:"TEA-01",priceCents:1250,qty:2},{sku:"MUG-07",priceCents:1800,qty:1},{sku:"BAD-00",priceCents:999,qty:-1}];console.log(`Total: ${formatPrice(cartTotal(items))}`);

The for loop with its if and continue became one || chain, and cents, currency, amount became single letters. Both versions print Total: $43.00. Here is the same input under each setting, measured in UTF-8 bytes. The gzip column is what this page's own counter reported in Chrome:

SettingBytesSavedGzip bytes
Original7630439
Whitespace and comments only (Compress and Mangle off)52930.7%335
Defaults45140.9%312
Defaults, license comment dropped42045.0%289
Defaults plus ES module38349.8%282

The gap between raw bytes and gzip bytes matters. Raw savings look dramatic because indentation and comments compress well anyway. If the page is served with gzip or Brotli, the real transfer saving with the defaults is 127 bytes, not the 312 that the raw count suggests.

Why top-level names stay long

In the defaults row, formatPrice, cartTotal and items keep their full names. That is deliberate. In a classic script, a top-level function or var becomes a property of the global object, and some other script tag, an inline onclick, or the console might call it by name. Renaming it would break those callers, and the minifier cannot see them.

Ticking ES module removes that guarantee. In the module row, Terser inlined both functions straight into the console.log call, because nothing outside the file can reach them. That is correct for a real module and wrong for a script that exposes helpers. If you want both small output and a public API, wrap the file in a function yourself and assign only the public names to window.

Newlines that already changed your code

Minifiers delete line breaks, which makes people worry that automatic semicolon insertion will bite. Terser parses first, so it never adds an ASI bug. What it does is show you one that was already there. Feed it this, with Compress and Mangle off:

let total = price
(discount || 0).toFixed(2)

and you get let total=price(discount||0).toFixed(2);. That is not the minifier joining two lines. JavaScript never inserted a semicolon after price, so the original was always a call to price. The same happens when a line starts with [ or a backtick. While testing this page, two of our own test snippets failed in exactly this way before minification. If a result looks like it merged two statements, put a semicolon at the end of the first line in your source and run it again.

Where this tool stops

For the rest of the page, the CSS minifier shrinks stylesheets the same way and the HTML formatter can minify markup. To turn a minified snippet into a bookmarklet, prefix it with javascript: and run it through the URL encoder.

FAQ

Does my JavaScript get uploaded?

No. The page loads a local copy of Terser from toolpetal.com the first time you use it, and minification then runs in this tab. Your code is never sent to a server, and nothing is saved after you close the page.

Which minifier engine does this page use?

Terser 5.51.2, the same engine webpack 5 uses for production JavaScript by default. It parses your code into a syntax tree before printing it back, so strings, regex literals, template literals and comments are handled by a real parser, not by search and replace.

Can it minify TypeScript or JSX?

No. Terser only accepts standard JavaScript. Type annotations, interfaces and JSX tags are syntax errors here. Compile with tsc, esbuild or Babel first, then paste the plain JavaScript output.

Why did my file barely shrink?

Usually the input was already minified, or most of its bytes are string data, or most names are top-level globals that are kept in script mode. Tick ES module only if the file really is a module, and compare the gzip figure, which is closer to what users download.