JavaScript Minifier: Compress JS Code Online Free
Your Bundle Is 847KB. Here's What Switching Minifiers Actually Does
A React SPA. Production build finishes in 38 seconds. The lead dev suggests switching from Terser to esbuild. "It's 10x faster," they say. The argument starts: does faster build time justify the risk of a larger output bundle?
The answer, per the privatenumber/minification-benchmarks project — the most cited JS minification benchmark on GitHub, last updated March 31, 2026 — is more nuanced than either side admits. On the React package (a representative real-world artifact), esbuild produces a gzipped output of 8.22KB in around 10–15ms. Terser produces 8.26KB in 215ms. The size difference? Under 50 bytes after gzip. The speed difference? 15x.
That's the core trade-off in JS minification in 2026: the best-compressing tools (UglifyJS, Terser) are also the slowest. The fastest tools (oxc-minify, esbuild) produce output within 1–2% of optimal. For most teams, that 2% size delta is irrelevant — but 20 minutes of saved CI time per day is real money.
This article gives you the data you need to make that call for your specific project — along with how to set up whichever tool you choose.
Key Takeaways
- ▸oxc-minify is the fastest JS minifier in 2026 (3ms on React), but @swc/core offers the best balance of speed and compression quality per March 2026 benchmarks.
- ▸UglifyJS produces the smallest gzipped output (8.18KB on React) but takes 420ms — 140x slower than oxc-minify. Worth it only when every byte matters.
- ▸Per the HTTP Archive 2025 Web Almanac, the median page ships 509KB of JS. A 40% reduction saves ~200KB, cutting Time to Interactive by 1–2 seconds on 3G.
- ▸Minification + gzip cuts JS transfer size by 70–85%. Brotli compression compounds this to 80–90% reduction.
- ▸Safe minification never changes runtime behavior. Aggressive optimization (dead code elimination, function inlining) can in edge cases — always run tests against minified output.
What JavaScript Minification Actually Does
Minification is a pipeline of transformations, each targeting a different source of bloat. Understanding which transformations are "safe" (semantically guaranteed) vs. "aggressive" (behavior-preserving only under certain assumptions) matters when debugging post-minification regressions.
Level 1: Cosmetic Removal (Always Safe)
Every minifier does this, and none of it can change behavior:
// Fetch user data from the API
async function fetchUser(userId) {
// Validate input
if (!userId || typeof userId !== 'string') {
throw new Error('Invalid user ID');
}
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return response.json();
}async function fetchUser(userId){if(!userId||typeof userId!=="string"){throw new Error("Invalid user ID")}const response=await fetch(`/api/users/${userId}`);if(!response.ok){throw new Error(`HTTP error: ${response.status}`)}return response.json()}Level 2: Identifier Mangling (Safe with Source Maps)
Renaming local variables from descriptive names to single characters. This is where the real size gains come from on large codebases. userId becomes n. response becomes r. The mangled names are shorter to type and compress better under gzip due to higher repetition density.
async function fetchUser(n){if(!n||"string"!=typeof n)throw new Error("Invalid user ID");const r=await fetch(`/api/users/${n}`);if(!r.ok)throw new Error(`HTTP error: ${r.status}`);return r.json()}Note that only local variables are mangled. Properties, exported names, and anything accessible from the outside world are left intact — mangling those would break runtime behavior. Terser's mangle.properties option can mangle properties too, but this requires careful configuration and allowlisting of external API surfaces.
Level 3: Compression Passes (Test Required)
Advanced minifiers apply multiple optimization passes before final output: dead code elimination, constant folding, function inlining, and loop unrolling. These can occasionally interact with side effects in unexpected ways. Terser's compress option exposes 50+ tuning parameters for this reason. The defaults are conservative; the "maximum compression" presets require testing.
2026 Minifier Benchmark: The Full Comparison
The privatenumber/minification-benchmarks project runs all major minifiers against a consistent test corpus and publishes results with every update. The March 2026 run used the React 19 package as the primary benchmark artifact.
| Minifier | Speed (React pkg) | Gzipped Output | vs Best Size | Written In |
|---|---|---|---|---|
| oxc-minify | 3 ms | 8.40 KB | +2.7% | Rust |
| @swc/core | 18 ms | 8.19 KB | +0.1% | Rust |
| esbuild | 12–20 ms | 8.22 KB | +0.5% | Go |
| Terser | 215 ms | 8.26 KB | +1.0% | JavaScript |
| UglifyJS | 420 ms | 8.18 KB | baseline | JavaScript |
| Google Closure | 1,400+ ms | ~8.10 KB | -1.0% | Java |
Source: privatenumber/minification-benchmarks, March 2026 update. React 19 package as test corpus. All times single-threaded on Apple M2. Gzip level 6.
Reading the Data Honestly
The spread between best (UglifyJS at 8.18KB) and worst (oxc-minify at 8.40KB) is 270 bytes after gzip. On a single file load over HTTP/2 with Brotli, that's imperceptible. The decision should be driven by build time at your project's scale.
The scoring methodology used by the benchmark project weights output size at 85% and speed at 15% by default — shifting to 50/50 when a tool is dramatically slow. Under this weighted score, @swc/core consistently ranks first for teams that run thousands of builds per month.
For teams that run one build per day, output size matters more than build speed. In that case, UglifyJS or Terser is the rational choice.
Each Tool, Honestly Assessed
Terser — The Production Standard
Terser is the default minifier in Webpack 5, Rollup, and Parcel. It's a fork of UglifyJS that added ES6+ support when UglifyJS stalled on ES5. According to npm download statistics, Terser receives over 50 million weekly downloads, making it the most-used JS minifier by a wide margin.
Terser's compress pipeline runs multiple passes: it evaluates constant expressions, removes unreachable branches, inlines small functions, and hoists variable declarations. This is why it produces smaller output than esbuild despite the speed disadvantage — esbuild explicitly opts out of multi-pass optimization to preserve single-pass speed.
Honest criticism: Terser is written in JavaScript and runs single-threaded. On a 500KB input file, it can take 2–5 seconds. For a monorepo with 50 entry points building in parallel, this becomes the build bottleneck. The tool has not been rewritten in a native language and has limited parallelism support.
esbuild — The Speed Record-Holder
esbuild is written in Go and uses parallelism aggressively — it saturates all CPU cores during builds. Evan Wallace (its author) published benchmarks showing 10–100x speed improvements over Webpack+Terser, which drove Vite to adopt esbuild as its development bundler and production minifier option.
esbuild's minifier is intentionally conservative — it runs in a single pass and avoids the complex optimization sequences that make Terser and UglifyJS slow. The trade-off is slightly larger output. On gzipped bundles, this difference almost always disappears into noise.
Honest criticism: esbuild's TypeScript support is transpile-only — it does not type-check. For projects that rely on type errors at build time, you need tsc --noEmit as a separate step. Also, esbuild has a stated policy of not supporting all Babel transforms, which can cause issues in legacy codebases.
@swc/core — The Emerging Standard
SWC (Speedy Web Compiler) is written in Rust and targets both transpilation and minification. Next.js adopted SWC as its default compiler in version 12, replacing Babel for most projects. The minifier component runs separately from the transpiler and produces near-optimal output at Rust speed.
Per March 2026 benchmarks, @swc/core achieves 8.19KB gzipped output on React (within 10 bytes of UglifyJS's best-in-class 8.18KB) while completing in 18ms. This makes it the first Rust-native minifier to match JavaScript-based tools on output quality while dramatically outperforming them on speed.
Honest criticism: SWC's minifier configuration API is less mature than Terser's. Some edge cases in its compress passes have caused regressions in real projects. The GitHub issues tracker has several open bugs related to minification of complex class hierarchies and generators.
oxc-minify — The New Rust Challenger
OXC (Oxidation Compiler) is a newer Rust-based JavaScript toolchain that includes a minifier component. Per the March 2026 benchmark, oxc-minify completes in 3 milliseconds — fastest of all tools tested. Its output (8.40KB gzipped) is slightly larger than SWC or Terser, but the 30–90x speed advantage over Terser makes it compelling for CI-heavy workflows.
Honest criticism: oxc-minify is the newest tool in this list and has the smallest production deployment footprint. The optimization passes are still maturing. For greenfield projects it's excellent; for legacy codebases, test thoroughly before deploying.
Build Tool Integration: The Exact Config You Need
Configuring a minifier through your build tool is the correct production pattern. Never commit minified JavaScript — always minify at build time from readable source.
Vite
Vite uses esbuild for minification by default in production builds. To switch to Terser (for maximum compression) or SWC:
import { defineConfig } from 'vite'
export default defineConfig({
build: {
// Default: 'esbuild' — fastest builds
// Switch to 'terser' for maximum compression (requires: npm i terser)
minify: 'terser',
terserOptions: {
compress: {
passes: 3, // More passes = smaller output, slower build
drop_console: true, // Remove console.log in production
drop_debugger: true,
pure_funcs: ['console.info', 'console.debug'],
},
mangle: {
safari10: true, // Fix Safari 10 let/const bug
},
format: {
comments: false, // Strip all comments including licenses
},
},
sourcemap: true, // Always generate source maps alongside minified output
},
})Webpack 5
Webpack 5 uses TerserPlugin by default. To switch to esbuild (dramatically faster builds):
const { EsbuildPlugin } = require('esbuild-loader')
module.exports = {
mode: 'production',
optimization: {
minimizer: [
new EsbuildPlugin({
target: 'es2020', // Target modern browsers; smaller output than es5
css: true, // Also minify CSS extracted by MiniCssExtractPlugin
}),
],
},
}Next.js
Next.js 12+ uses SWC by default. If you have a .babelrc file, Next.js falls back to Babel — a common performance trap. To force SWC and configure minification:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
// SWC minification is the default in Next.js 13+
// swcMinify: true is now the default (no longer needed to explicitly set)
// To use Terser instead (not recommended — SWC is faster and equally good):
// experimental: { forceSwcTransforms: true }
compiler: {
// Remove console.log statements in production
removeConsole: {
exclude: ['error', 'warn'],
},
},
}
export default nextConfigRollup
import terser from '@rollup/plugin-terser' // Option 1: max compression
import esbuild from 'rollup-plugin-esbuild' // Option 2: max speed
export default {
input: 'src/index.ts',
output: {
file: 'dist/bundle.min.js',
format: 'esm',
sourcemap: true,
},
plugins: [
// Use ONE of these:
terser({ compress: { passes: 2 }, mangle: true }), // Smaller output
// esbuild({ minify: true, target: 'es2020' }), // Faster build
],
}Online JS Minification: When to Use It
Online minifiers serve a specific niche: quick, one-off compression of a single file when you don't have a build pipeline set up — prototyping, CDN snippets, or minifying a vendored library you're patching manually.
The security-conscious approach: use a client-side tool that runs the minifier in your browser via WebAssembly. BytePane's JavaScript minifier tool runs Terser compiled to WASM — your code never leaves the browser tab. Avoid tools that upload your source to a server unless you've verified their data handling policy.
Use online minification when:
- ✓Minifying a single file for a quick demo or CDN upload
- ✓Checking what a minifier does to a specific code pattern
- ✓Compressing a vendored third-party library you can't rebuild from source
Don't use online minification when:
- ✗You have a build pipeline — let it handle minification automatically
- ✗The file contains proprietary business logic and the tool uploads to a server
- ✗You're tempted to commit the minified output — always commit source, minify at build time
Source Maps: Non-Negotiable for Production Debugging
Every minifier covered here generates source maps. A source map is a JSON file that maps positions in the minified output back to corresponding lines in the original source. Without it, a production error stack trace looks like:
TypeError: Cannot read properties of undefined (reading 'map')
at r (main.a7f3b2c.js:1:4291)
at n (main.a7f3b2c.js:1:7823)With source maps uploaded to your error tracker (Sentry, Datadog, etc.) the same error becomes:
TypeError: Cannot read properties of undefined (reading 'map')
at ProductList.render (src/components/ProductList.tsx:47:12)
at CartContext.Provider (src/context/CartContext.tsx:103:8)Always generate source maps in production. Never expose them publicly via your web server (they contain your full source code). Upload them to your error tracker using their CLI or CI integration, then configure your server to block direct access to *.map files.
location ~* \.map$ {
deny all;
return 404;
}Minification Alone Won't Save You: Tree Shaking First
A common misconception: "just minify harder and the bundle will be smaller." Switching from esbuild to UglifyJS saves a few hundred bytes. Properly tree-shaking an import * as _ from lodash to individual imports can save 70–500KB.
Tree shaking removes unused exports from ES modules before minification runs. The minifier then compresses what's left. The correct optimization sequence is:
- Audit your bundle with
webpack-bundle-analyzerorvite-plugin-visualizer— find which dependencies are largest - Tree shake aggressively — replace barrel imports with direct imports, mark packages as
sideEffects: falsein package.json - Code split — lazy-load routes and large dependencies behind
React.lazy()or dynamicimport() - Minify the result — now the minifier is working on a smaller, cleaner input
- Enable Brotli compression — compounds with minification for maximum transfer savings
The HTTP Archive 2025 Web Almanac reports that the median JavaScript page weight is 509KB transferred — but 67% of pages ship more than 1MB of JS before gzip. Minification accounts for 30–60% savings on raw bytes; tree shaking and code splitting can eliminate entire megabytes that never needed to be shipped at all.
For a broader look at JS performance optimization in the full stack, see BytePane's web performance checklist.
Frequently Asked Questions
What is JavaScript minification?
JavaScript minification removes whitespace, comments, and renames local variables to shorter names without changing program behavior. The result is a smaller file that downloads and parses faster. Minified output is semantically identical to source — just harder for humans to read. Production builds should always minify JS; development builds should not, to keep stack traces readable.
Which JavaScript minifier is fastest in 2026?
Per the privatenumber/minification-benchmarks project (updated March 2026), oxc-minify is fastest at 3ms on the React package. @swc/core is 18ms. esbuild completes in 10–20ms. Terser runs at 215ms. UglifyJS is slowest at 420ms but produces the smallest output. The right choice depends on whether you optimize for build speed or output size.
Does minification affect how JavaScript runs?
Safe minification never changes runtime behavior. Whitespace removal, comment stripping, and identifier shortening are purely cosmetic. However, aggressive optimizations — like inlining functions or removing dead branches — can theoretically affect edge cases involving side effects. Always run your test suite against minified output in CI.
Should I use Terser or esbuild for Webpack?
Webpack 5 ships TerserPlugin as the default. For most projects this is fine — Terser produces excellent output quality. If build time is a bottleneck, switch to esbuild-minimizer-webpack-plugin. esbuild runs 10–20x faster than Terser with under 2% size difference on gzipped bundles.
What is the difference between minification and uglification?
"Uglification" is minification plus identifier mangling — renaming variables from descriptive names to single letters. Mangling adds meaningful size savings on top of whitespace removal, but makes stack traces in production nearly unreadable. Always configure source maps alongside uglification so you can still debug production errors.
How much does JavaScript minification reduce file size?
Minification alone reduces raw JS by 30–60%. Combined with gzip, total transfer savings reach 70–85%. Brotli compression achieves 80–90% reduction from the original. Per HTTP Archive 2025 Web Almanac, the median page ships 509KB of JS — a 40% reduction saves ~200KB, cutting Time to Interactive by 1–2 seconds on 3G.
What is tree shaking and is it the same as minification?
Tree shaking and minification are complementary but distinct. Tree shaking removes entire exported functions never imported anywhere — dead code elimination at the module graph level. Minification compresses what remains. Tree shaking happens during bundling (Rollup, Webpack, esbuild), minification as a final pass. Do both for optimal bundle size.
Minify JavaScript Instantly
Paste your JS and compress it in one click. Runs Terser in WebAssembly — your code never leaves your browser.
Open JavaScript Minifier →