CSS & JavaScript Minifier

Minify and compress CSS and JavaScript code

What a Minifier Actually Does — and What It Buys You After gzip

Serious JavaScript minifiers (terser, esbuild, swc) do far more than delete whitespace. They parse the source into an AST and then apply semantics-preserving transforms: identifier mangling (renaming local variables to one letter — safe because scope is known), constant folding (1000 * 60 * 60 becomes 3600000), dead-code elimination (an if (false) branch disappears), boolean and property shorthand rewrites (true becomes !0, obj['key'] becomes obj.key), and function inlining. Calls annotated with a pure comment marker can be dropped entirely if their result is unused — that is how tree-shaken libraries reach their advertised sizes. The numbers matter because gzip changes the story. Minification alone typically removes 30–60% of raw bytes from application code. But gzip is extremely good at compressing whitespace and repeated long identifiers, so the delta after compression shrinks: a file that minifies from 400 KB to 160 KB might gzip to 52 KB minified versus 61 KB unminified. That remaining 15–20% is still worth having — and mangling helps compression less than you would hope, because short names reduce redundancy that gzip would have exploited anyway. The practical rule: measure the compressed size, not the raw size. A minification step that saves 40% raw but 3% gzipped may not justify the source-map complexity it introduces. Conversely, dead-code elimination and tree shaking often save more compressed bytes than all whitespace removal combined, because deleted code compresses to nothing.
# same bundle, four ways (typical mid-size app)
source                412,000 bytes
minified              168,000 bytes   (-59% raw)
minified + gzip        52,000 bytes
minified + brotli-11   46,000 bytes

gzip WITHOUT minify  ≈ 61,000 bytes
# → minification still buys ~15% after compression,
#   mostly from dead-code elimination, not whitespace

Regex vs AST: Why Byte-Level Minification of JavaScript Is Unsafe

JavaScript cannot be safely minified with string replacement, and the reasons are structural. First, the slash character is ambiguous: a / b is division, but /b/.test(a) is a regular expression literal. Which one a given slash means depends on the parse state, which a regex-based replacer does not have. Strip the "comment" inside a regex literal like /https?:\/\//, and the code silently changes meaning. Second, automatic semicolon insertion (ASI) makes newlines semantically significant. The statement return followed by a newline and an object literal returns undefined — ASI inserts a semicolon after return. Naively joining lines can either hide that bug or, worse, create new ones: a line ending in a variable followed by a line starting with an opening parenthesis or bracket fuses into a function call or a property access. Real minifiers parse first, so they know where semicolons are required and can even exploit ASI to drop them safely. Third, strings and template literals are opaque containers. A string containing two slashes is not a comment; whitespace inside a template literal is data and must survive byte-for-byte. The conclusion for a quick online minifier like this one: it is fine for trivial snippets you can eyeball afterwards — a bookmarklet, an inline event handler, a short style block. For anything that ships to users, run terser, esbuild or swc in your build. esbuild minifies roughly 10–100x faster than terser and produces output only about 1–2% larger; terser squeezes hardest; swc sits in between with excellent TypeScript support.
// 1. ASI: this returns undefined, not the object
function pick() {
  return          // <- semicolon auto-inserted here
    { ok: true };
}

// 2. line joining creates a call that never existed
const total = fn
(a || b).forEach(log)   // joined: fn(a || b).forEach(log)

// 3. slash ambiguity — is it division or a regex?
const x = a / b / c;        // two divisions
const ok = /a\/b/.test(s);  // one regex containing a slash
// a byte-level replacer cannot tell these apart

CSS Minification: The Safe Transforms and the calc() Trap

CSS is a simpler language than JavaScript, so more of it can be minified mechanically — but not all. The reliably safe transforms: stripping comments (except license comments that start with /*!, which build tools preserve by convention), collapsing whitespace outside strings, dropping the final semicolon in each declaration block, shortening six-digit hex colors with paired digits (#ffffff to #fff), removing units from zero lengths (0px to 0 — valid because zero is zero in any length unit), and merging four identical box values (margin: 4px 4px 4px 4px to margin: 4px). The classic trap is calc(): the CSS syntax requires whitespace around + and - inside calc, because a minus glued to a number parses as a negative value rather than a subtraction. calc(100% - 20px) is valid; calc(100%-20px) is not, and browsers drop the whole declaration silently. Similar whitespace sensitivity exists in media queries (screen and (min-width: ...)) and in CSS custom properties, whose values are stored token-for-token — minifying inside var() fallbacks or custom property values can change what a consumer reads. Also non-obvious: 0% is not always replaceable with 0 (in flex-basis and some animation contexts the percentage matters), and removing quotes from font names or url() values is only safe when the content contains no reserved characters. In real pipelines this is handled by cssnano (PostCSS-based, very configurable) or Lightning CSS (Rust, extremely fast, also transpiles modern syntax for older browsers). Both parse to an AST, which is why they can safely do the transforms above and skip them where context forbids.
/* before */
.card { margin: 0px 0px 0px 0px; background: #ffffff; }
.col  { width: calc(100% - 20px); }

/* correct minify */
.card{margin:0;background:#fff}.col{width:calc(100% - 20px)}

/* BROKEN: whitespace inside calc() is syntax, not formatting */
.col{width:calc(100%-20px)}   /* declaration silently dropped */

/*! preserved license header — the ! is the convention */

Source Maps: Minify Without Losing the Ability to Debug

A production stack trace pointing at line 1, column 48213 of bundle.min.js is useless. Source maps solve this: a .map file (JSON with a VLQ-encoded mappings string) records, for every generated position, the original file, line, column, and often the original identifier name. The minified file references it via a sourceMappingURL comment on its last line, and browser devtools fetch it lazily — normal visitors never download it. The deployment question is who gets to see the map. Three common setups: (1) public maps, simplest, but they expose your original source to anyone curious; (2) hidden maps — generate them, do not reference them in the bundle, and upload them to your error tracker (Sentry, Datadog, Rollbar) which symbolicates stack traces server-side; (3) restricted maps served only to authenticated internal IPs. For closed-source products, hidden maps are the standard answer; note that shipping maps that embed sourcesContent includes the full original source text inside the map file itself. Two practical gotchas. Every transform step in the chain must produce and consume maps (TypeScript to esbuild to terser), otherwise the final map points at an intermediate artifact and every frame resolves to the wrong place. And when you re-release, maps must match byte-exactly the bundle they were generated with — error trackers key maps by a release identifier or content hash for exactly this reason. If your symbolicated traces look plausible but subtly wrong, a stale map with a mismatched release is the usual culprit.
// last line of bundle.min.js
//# sourceMappingURL=bundle.min.js.map

// hidden-maps workflow (Sentry example)
esbuild app.ts --minify --sourcemap=external --outfile=dist/app.js
sentry-cli sourcemaps upload --release=v1.4.2 ./dist
rm dist/*.map        # maps never reach the CDN

// stack trace before / after symbolication
at t (bundle.min.js:1:48213)
at checkout (src/cart/checkout.ts:87:11)   // with map + matching release

Minify, Compress, Bundle: Three Different Jobs, Do All Three

Teams regularly conflate three optimizations that operate at different layers. Minification rewrites the source text once, at build time. Transfer compression (gzip or Brotli) encodes the bytes per response, at serve time, and the browser decodes transparently. Bundling merges modules to cut request overhead and enable cross-module tree shaking. They compound — none replaces another. Compression settings matter more than most people think. For static assets, precompress at maximum level during the build (Brotli quality 11, gzip 9) and let the server pick the encoding via content negotiation; Brotli-11 is far too slow to run per-request but is free when done once at deploy. For dynamic responses, use Brotli 4–5 or gzip 6 — the higher levels burn CPU for single-digit percent gains. Typical text ratios: gzip about 3–4x, Brotli about 10–20% smaller than gzip on JS and CSS. Order of operations for an inline snippet workflow (the case this tool serves): keep the readable source in the repo, minify the copy you paste into the HTML attribute, GTM container, or email template, and verify the result still parses — paste it into the browser console for JS, or a validator for CSS. Never treat the minified text as the editable original; a one-way artifact you can regenerate is harmless, a hand-edited minified blob is technical debt with no undo.
Last updated:

About this tool

A CSS / JavaScript minifier strips comments, whitespace, and redundant tokens to shrink source files for production. Smaller bundles transfer faster, parse faster, and cost less to serve from a CDN. Modern build pipelines do this automatically, but a quick web minifier is handy for inline snippets, email styles, or tiny utility scripts that never touch a bundler.

How to use

  1. Choose CSS or JavaScript mode at the top.
  2. Paste your source into the input panel.
  3. Click Minify — the output appears on the right with the size saving in bytes and percent.
  4. Copy the minified result to embed in a <style> or <script> tag.
  5. Use the original source as the long-term editable copy and only ship the minified version.

Common use cases

  • Inlining critical CSS in the <head> for faster First Contentful Paint.
  • Shrinking a Google Tag Manager custom HTML snippet to the size limit.
  • Compacting a JS one-liner you want to embed in a bookmarklet.
  • Reducing the size of email-safe CSS where every byte matters.
  • Trimming a no-bundler vanilla JS file before deploying to a static host.
  • Estimating the bundle-size impact of a snippet before adding it to a build.

Frequently asked questions

Q. Is this safe for production JavaScript?

A. For trivial scripts yes. For real applications use a battle-tested minifier like esbuild, terser, or swc inside your build pipeline so source maps and ECMAScript edge cases are handled.

Q. Why does the output break my CSS?

A. Most often a missing semicolon in the source. CSS minification assumes valid input — fix the source, then minify.

Q. Does minifying obfuscate my code?

A. Slightly — names are not changed, only whitespace is stripped. For real obfuscation use a dedicated tool, but understand it does not provide real security.

Q. Can I minify HTML the same way?

A. HTML minification needs different rules (preserved whitespace in <pre>, attribute quoting, etc.). Use a dedicated HTML minifier for that.