To minify JavaScript, run it through a minifier such as Terser or esbuild, or let your bundler do it when you build for production. On the command line that's terser input.js -c -m -o output.min.js or esbuild input.js --minify. Keep the original file as your source, ship the minified one, and generate a source map so errors still point at real line numbers.
What minification does to your code
Minification shrinks JavaScript without changing what it does. MDN defines it as "removing unnecessary or redundant data without affecting how a resource is processed by the browser". In practice that means dropping comments, whitespace and unused code, and shortening variable and function names.
Here's a small function before:
// Return the total price including tax
function calculateTotal(price, taxRate) {
const tax = price * taxRate;
return price + tax;
}
And roughly what a minifier with compression and mangling turns it into:
function calculateTotal(t,a){return t+t*a}
The comment and spaces are gone, the local names are single letters, and the tax variable got folded into the return. The function's own name stayed, because it's declared at the top level.
Google's Lighthouse audit puts the benefit plainly: "Minifying JavaScript files can reduce payload sizes and script parse time." Its Opportunities section lists each unminified file with the possible savings in KiB, which is a quick way to find files you missed.
Option 1: Minify JavaScript with Terser
Terser is a standalone minifier, and webpack v4 shipped a Terser plugin by default. Lighthouse calls it "a popular JavaScript compression tool."
Install it and run it:
npm install -D terser
npx terser input.js -c -m -o output.min.js
From the Terser CLI docs:
-c(--compress) turns on the compressor, which rewrites code into shorter equivalents.-m(--mangle) turns on the mangler, which renames local variables.-osets the output file. Leave it off and the result goes to standard output.
Names in the top-level scope aren't mangled by default, because another script on the page might use them. If your file is a self-contained bundle, add --toplevel to compress and mangle those too.
For a source map, the docs say to pass --source-map --output output.js, and the map is written to output.js.map.
Option 2: Minify with esbuild
esbuild minifies as part of bundling. One flag does it:
npx esbuild app.js --minify --outfile=app.min.js
The esbuild docs split --minify into three parts: whitespace, identifiers and syntax. If one of them causes trouble, turn on the other two separately.
Option 3: Let your bundler do it
If you build with Vite or webpack, you're probably minifying already. Vite's build.minify defaults to 'oxc' for client builds and false for SSR builds. Vite's docs say the Oxc minifier is "30 ~ 90x faster than terser and only 0.5 ~ 2% worse compression."
To use Terser instead, install it (npm add -D terser) and set build.minify: 'terser'. Vite notes that "esbuild or Terser must be installed" for those settings, and that 'esbuild' is deprecated. Pass Terser flags through build.terserOptions.
Option 4: Paste it into an online minifier
For a single snippet, such as a bookmarklet or a script tag on a landing page, a browser tool is quicker than setting up a build. The JavaScript minifier on OnlineViewer Dev runs in the page, and its tool code makes no network requests with what you paste. For a real project, use a build step: you want minification to happen every time, not when someone remembers.
Why minified code breaks, and how to fix it
Minified JavaScript usually breaks for one reason: some code reads a function or class name at runtime, and the minifier renamed it. Think of a dependency injection container that looks up services by class name, or logging that prints this.constructor.name. After mangling, UserService becomes e, and the lookup fails with no syntax error anywhere.
Terser has two flags for this. --keep-fnames is documented as "Useful for code relying on Function.prototype.name," and --keep-classnames does the same for classes. esbuild's equivalent is --keep-names. Its docs say "some frameworks rely on the name property for registration and binding purposes," and the flag preserves the original names even in minified code.
The other common break is top-level mangling. If you pass --toplevel and an inline onclick="init()" calls a global function, that global now has a different name.
Minification and gzip are separate steps
Minification and compression solve different problems, and you want both. Minification rewrites the code at build time. HTTP compression squeezes the file on its way to the browser. MDN's compression guide says two formats matter today: gzip, "the most common one," and br, Brotli. It recommends turning compression on for everything except files that are already compressed, like images and video.
Check that your server or CDN sends Content-Encoding: gzip or br for .js files.
How to check it worked
- Open the built file. It should be one long line, or a few, with no comments.
- Run Lighthouse and check that the "Minify JavaScript" opportunity is gone.
- Load the page and open the console. A
TypeErrorthat wasn't there before often means a name your code relied on got mangled. - Throw a test error and check the stack trace in dev tools points at your original file. If it doesn't, the source map isn't being served.
My take
Leave minification to the bundler's default. Reach for Terser options only when something breaks or bundle size really matters. Hand-tuning compressor flags buys a few percent at best, and every custom option is one more thing to debug. The limitation: a default minifier won't tell you when it renames something your code reads at runtime. You find out from a bug report, so test the production build, not just the dev server.
FAQ
What does minifying JavaScript actually remove?
Comments, whitespace and unused code, and it shortens variable and function names. The code still runs the same way; it's just smaller.
Is minifying the same as gzip or Brotli compression?
No. Minification rewrites the code itself at build time. gzip and Brotli compress the file during transfer. Most sites use both.
Why does my code break after minification?
Usually because something reads a function or class name at runtime, and the minifier shortened it. Use Terser's --keep-fnames and --keep-classnames, or esbuild's --keep-names.
Can I debug minified JavaScript?
Yes, with a source map. Terser writes one with --source-map, and browser dev tools use it to show the original code and line numbers.