Beautify or Minify: What Each Is For, and What It Does to the Weight
Published 8/10/2026 · 14 min read · Developer tools
Daniel Okonkwo — Front-end developer and tech writer at Allin
Web performance · File formats
Checked against 4 sources
Beautifying is for reading; minifying is for shipping. The size argument for minifying is much weaker than the raw byte count suggests, because everything you ship is gzipped and gzip already handles repetition — and indentation is the most repetitive text there is. Four stylesheets were run through this minifier and both sizes recorded. A hand-written card component went from 1 612 to 1 118 bytes, a 30.6% cut; gzipped it went from 659 to 555, a saving of 104 bytes. Splitting the two passes apart is the real lesson. On the same four files, removing whitespace alone saved 48, 103, 104 and 147 gzipped bytes — roughly a hundred, whatever the file size, from 1.6 KB up to 22 KB. Removing comments alone saved 57, 1 358, 2 420 and 1 042. On a heavily commented file the comments are 96% of the real saving; the whitespace is a rounding error. So minify for the comments and the dead code, not for the newlines, and never at the cost of correctness. This particular minifier is five regular expressions and it breaks five things: it deletes the whitespace CSS requires on both sides of + inside calc(), so calc(100% + 16px) becomes an invalid declaration the browser drops; it edits the inside of strings, turning content: "a; b" into "a;b"; it deletes a string that merely looks like a comment; it eats a comment sequence inside a data URI; and it rewrites a[title="hello, world"]. The same site ships css-compressor, a scanner that breaks none of them and is 2 bytes larger on a 22 746-byte file.
Four real stylesheets run through the minifier, measured raw and after gzip. Stripping every space saved 48, 103, 104 and 147 compressed bytes; stripping comments saved 57, 1 358, 2 420 and 1 042. Plus the five inputs this minifier breaks.
The measurement most articles skip: after gzip
A minifier reports the raw byte count because that is the number it can compute. It is not the number that travels. Every stylesheet served over HTTP arrives compressed, and the two operations overlap: gzip encodes a run of repeated bytes as a short back-reference, and four spaces of indentation repeated three hundred times is the cheapest thing it will ever see. Removing that indentation before compressing removes work gzip was doing for free.
So the two passes were separated and each measured on its own. Removing only comments, then removing only whitespace, then both, on four files: a hand-written card component of 1 612 bytes, and three stylesheets taken straight out of this site's own repository. Compressed with gzip at level 9, the whitespace pass saved 48, 103, 104 and 147 bytes. Not percentages — bytes, and roughly the same hundred bytes whether the file was 1.6 KB or 22 KB, because there is only so much distinct indentation in a stylesheet no matter how long it gets. The comment pass, on the same four files, saved 57, 1 358, 2 420 and 1 042 compressed bytes.
One file makes the point on its own. The site's globals.css is 7 148 bytes, of which a great deal is prose explaining why each colour was chosen. Stripping whitespace took it from 3 140 gzipped bytes to 3 036 — 104 bytes, 3.3%. Stripping comments took it to 720 — a saving of 2 420 bytes, 77%. Ninety-six per cent of the real gain came from the comments, and none of it came from the newlines. That is the whole argument in one file: minify to drop what the browser cannot use, not to drop what the compressor already handles.
Beautifying costs almost nothing once it is compressed
The symmetry holds in the other direction, which is the useful part. The HTML beautifier's own example is a minified page of 485 bytes. Beautified with two-space indentation it becomes 627 bytes — 142 more, a 29.3% increase, the number that makes people nervous. Gzipped, it goes from 343 to 369 bytes: 26 bytes, 7.6%. Twenty-six bytes is nothing. If you are shipping a readable page for a reason — a documentation sample, an email template someone has to edit, a page you want other people to be able to view-source — the compressed cost of leaving it readable is smaller than one favicon request.
The HTML beautifier here is also worth understanding for what it refuses to do. It copies the contents of pre and textarea byte for byte, because their whitespace is rendered. It keeps a single space between two inline elements, because deleting it glues two words together on screen. And it leaves a quoted attribute alone, so title="a > b" survives intact rather than being cut at the angle bracket. Those three behaviours were checked directly and all three hold.
It does have one blind spot, and it is the one its own comment claims to have closed. Two buttons separated by a space — <div><button>A</button> <button>B</button></div> — come back from the minifier as <button>A</button><button>B</button>. Buttons are inline-block, so that space is drawn, and the gap between the two buttons disappears. The rule the minifier applies is that whitespace between two block-level tags is invisible, and its list of inline elements holds a, b, span, code and two dozen others but not button, label's siblings like select, or img inside a link. Check any row of buttons after minifying.
Five inputs this CSS minifier gets wrong
The css-minifier page is five regular expressions: drop comments, collapse runs of whitespace to one space, remove the spaces around a set of punctuation characters, drop a semicolon before a closing brace, trim. That is enough for a stylesheet you wrote yourself an hour ago and not enough for anything else, because a regular expression cannot tell structure from content.
The first failure is the serious one. CSS Values and Units Level 3 says, in the syntax of calc(), that white space is required on both sides of the + and - operators. The punctuation list here includes +, so calc(100% + 16px) comes out as calc(100%+16px) and the whole declaration is invalid: the browser discards it and falls back to whatever was there before. Because - is not in the list, calc(100% - 16px) survives untouched, which is worse — half your calc expressions work and half silently vanish. This is not a made-up case. The file apps/web/components/app/app-shell.module.css in this repository contains calc(74px + env(safe-area-inset-bottom)), the padding that keeps the mobile tab bar clear of the home indicator. Run it through this tool and that padding is gone.
The other four all come from the same root: strings and data URIs are content, and the passes treat them as structure. content: "a; b" becomes content:"a;b", because the semicolon is on the punctuation list. content: "{ }" becomes content:"{}". a[title="hello, world"] becomes a[title="hello,world"], and the selector stops matching. A stylesheet whose content string contains the characters that open and close a CSS comment — content: "/* not a comment */" — comes back as content:"", the string emptied. And a background image written as a data URI that happens to contain the same two-character sequence loses everything between them: url("data:image/svg+xml,...%3E/*x*/%3C...") arrives with the middle deleted and the image dead.
Two things it does not break, for balance. Custom properties survive: --shadow: 0 2px 8px rgba(0, 0, 0, 0.1) comes out as --shadow:0 2px 8px rgba(0,0,0,0.1), which is the same value. Media queries survive too: @media screen and (min-width: 600px) becomes @media screen and (min-width:600px), still valid, and the modern range syntax @media (400px <= width <= 700px) is left alone because <= is not on the punctuation list.
Two minifiers on the same site, and the safe one is 2 bytes bigger
The css-compressor page uses a different engine. Instead of matching patterns it walks the file character by character and marks each character as structure or literal — anything inside a string, a comment, or a pair of parentheses is literal, and no transform is allowed to touch it. That single flag is what keeps a data URI, a calc() expression and the decimals of an rgba() intact.
The whole point of the naive approach was supposed to be that it is smaller. It is not. Both were run on the same three real stylesheets. On app-shell.module.css, 22 746 bytes in, the regex version produced 19 278 bytes and the scanner 19 280 — a difference of 2 bytes, and gzipped the scanner was actually 1 byte smaller, 4 191 against 4 192. On admin.css the gap was 1 byte raw and 2 bytes gzipped. On globals.css it was 8 bytes raw. Eight bytes, on a file where the safe version keeps the space inside @media (min-width: 600px) and inside rgba(0, 0, 0, 0.1). There is no size argument for the regex version at all; it is simply the one that occasionally destroys the file.
SVG is the exception, and it has its own bug
Everything above says the weight argument for minifying is weak. SVG is where it is strong, because the weight in an exported SVG is not whitespace at all. A small drawing saved from a vector editor carries an XML declaration, an editor comment, a title, a description, an RDF metadata block, a named view with the last zoom level, two editor namespaces, an identity matrix transform, a stroke-width and an opacity set to their own defaults, and coordinates written to seven decimal places. Run one such file — 1 071 bytes — through svg-optimizer and it comes out at 256 bytes, a 76.1% cut. Gzipped: 603 down to 207, a 65.7% cut. That saving is real because the removed material is unique text, and unique text is exactly what a compressor cannot help with.
That same run turned up a defect worth knowing before you use it. Colours pass through two steps in the wrong order: they are shortened first, then handed to the number-rounding pass, which reads hex digits as numbers. stroke="#000000" is shortened to #000 and then rounded to #0. black becomes #0. red becomes #f0. And because the number pattern accepts scientific notation, a hex colour whose digits straddle an e explodes: #e5e7eb comes out as #e50000000eb, #1e293b as #1e+293b, #0e7490 as #0. The same pass rewrites url(#g-0010) to url(#g-10) while leaving id="g-0010" alone, so the gradient it points at disappears. All of this was reproduced in a real browser, with the default settings. Switching off the round numbers option avoids every one of them, at the cost of a few bytes of precision.
Two more things about the same tool. It removes title and desc as editor metadata — those are the two elements a screen reader announces for an SVG, so an icon that was accessible stops being so. And its check for malformed markup is the careful kind: a browser reports an XML error by grafting in an element named parsererror, so hunting for that name alone would refuse any valid drawing carrying one of its own. The tool asks the engine instead — once per session it parses something deliberately broken, reads the namespace of the marker that comes back, and looks only there afterwards. A drawing that contains a parsererror element optimises normally; an unclosed tag is still refused.
| File | Original, raw / gzip | Whitespace only, gzip saved | Comments only, gzip saved |
|---|---|---|---|
| Hand-written card component | 1 612 / 659 | 48 bytes | 57 bytes |
| app-shell.module.css (dense, few comments) | 22 746 / 5 661 | 103 bytes | 1 358 bytes |
| globals.css (heavily commented) | 7 148 / 3 140 | 104 bytes | 2 420 bytes |
| admin.css | 4 767 / 1 855 | 147 bytes | 1 042 bytes |
| Editor-exported SVG, whole optimiser | 1 071 / 603 | Metadata, not whitespace | 396 bytes, 65.7% |
Frequently asked questions
- Is minifying CSS still worth doing if the server gzips everything?
- Yes, but for the comments, not the spaces. On the four files measured here, removing every space and newline saved 48, 103, 104 and 147 gzipped bytes — about a hundred bytes each, regardless of whether the file was 1.6 KB or 22 KB, because gzip encodes repeated indentation almost for free. Removing the comments saved 57, 1 358, 2 420 and 1 042 gzipped bytes on the same files. If your stylesheet is documented, the comments are the whole saving; if it is not, minifying it will buy you roughly a tenth of a kilobyte and you should spend the effort on an unused-CSS pass instead. What minifying is genuinely for, on a build pipeline, is that it comes bundled with the passes that do matter: removing rules nothing on the page uses, merging duplicate declarations, and shortening colours and units.
- My layout broke after minifying. Where do I look first?
- Search the minified file for calc( and read every one. If any of them contains a plus sign with no spaces around it — calc(100%+16px) — that declaration is invalid and the browser is ignoring it. CSS requires whitespace on both sides of + and - inside calc(), and a regex minifier that tightens the spacing around punctuation deletes it. Next, search for content: and for any attribute selector containing a comma or a semicolon, because a naive minifier edits the inside of strings. Then look at data URIs: if one contained the two characters that open a CSS comment followed later by the two that close one, everything between them has been removed. Finally, compare the rule count before and after; if it dropped, something structural was lost, not just whitespace.
- Should I beautify a minified file I did not write, before editing it?
- Yes, and it is the honest use of a beautifier. Reformatting changes only whitespace, so it cannot alter behaviour, and it turns a one-line file into something a diff can describe. Two cautions. First, a beautified file is not automatically a source file — if the original was generated from Sass, TypeScript or a component library, editing the output means your edit disappears at the next build. Second, beautifying then re-minifying is not always the identity function: on the HTML engine tested here, a round trip adds one space either side of the text inside every non-inline element, growing a 133-byte page to 145 bytes and then staying stable. Harmless in the browser, but do not expect a byte-identical file back.
- Why does the SVG optimiser save so much more than the CSS minifier?
- Because it removes different material. A CSS minifier removes whitespace and comments; a compressor already handles whitespace, so only the comments count. An SVG optimiser removes editor metadata, default attributes, empty groups and excess decimal places — all of it unique text a compressor cannot fold away. In the file measured here, 1 071 bytes became 256 raw and 603 gzipped bytes became 207, a 65.7% cut that survived compression almost intact. The lesson generalises: any minifier that only reformats will disappoint you after gzip, and any minifier that deletes content will not. That is also why deleting content is the part to check.
- Is Brotli different enough from gzip to change the answer?
- No, it makes it slightly stronger. The card stylesheet compressed to 527 bytes with Brotli against 659 with gzip, and minified it reached 451 against 555. The absolute saving from minifying was 76 bytes under Brotli and 104 under gzip: a better compressor leaves less for the minifier to remove, exactly as you would expect, since both are removing the same redundancy. So if your host serves Brotli — most CDNs do — the case for minifying whitespace is weaker still, and the case for removing comments is unchanged, because a comment is unique text under either algorithm.
Articles you may find interesting
All guides →Related tools
These figures come from running these tools on real files and compressing the result, not from a vendor's claim. Byte counts depend entirely on the file: a stylesheet written with long comments compresses differently from one written without them, and your numbers will not be ours. Minifiers are also not all equivalent — two on this site disagree about the same input — so treat any minified output as new code that has to be looked at before it ships. Keep the readable original in version control, minify at build time, and check the page in a browser before you publish.
Sources
- W3C — CSS Values and Units Module Level 3, section 8.1.1 — the syntax of calc(), which states that white space is required on both sides of the + and - operators (the * and / operators may be used without it)
- W3C — CSS Syntax Module Level 3 — the tokenizer: how a string token, a comment and a url() token are recognised, and why a transform that does not run the tokenizer cannot tell a semicolon in a string from a declaration terminator
- IETF — RFC 1952, GZIP file format specification version 4.3 — the DEFLATE-based format used for the Content-Encoding: gzip of every stylesheet measured here, and the back-reference mechanism that makes repeated indentation nearly free
- W3C — Scalable Vector Graphics (SVG) 2 — the title and desc elements and their role in accessible names and descriptions, which is why an optimiser that strips them as editor metadata changes what a screen reader announces
Spotted a mistake in this article?