The whitespace problem is the whole problem
Indenting a JSON file is boring. Indenting HTML is not, because HTML is the rare format where whitespace sometimes disappears and sometimes renders as a visible space — and the rule for which is which depends on the element, on its content model, and on CSS. A formatter that ignores this produces output that looks tidier and reads differently in the browser. The damage is subtle enough that it usually ships.
The clearest example takes two lines. Write
<a>first</a><a>second</a> and the two links
touch, with nothing between them. Put a newline between the tags to make it
prettier and the browser collapses that newline into a single space, so the links
now sit apart. Nothing in the markup warns you; the diff looks like formatting, the
rendering changed. The same trap catches
<strong>10</strong><span>%</span>, breadcrumb
separators, icon-plus-label buttons and every price built from two inline spans.
So the rule this tool follows is narrow and mechanical: a line break may only replace whitespace that was already there, or sit at the edge of a block-level box, where the CSS line-box algorithm removes leading and trailing collapsible whitespace anyway. Where two inline items touch, they keep touching — even if that leaves a line far past your chosen width. A long line is a cosmetic problem. A moved space is a rendering bug.
One more detail that trips up regular-expression formatters: in JavaScript,
\s matches U+00A0, the character produces. That
character exists precisely because it does not collapse. Any formatter built
on \s quietly eats the non-breaking spaces you fought to keep. This one
treats only space, tab, newline, carriage return and form feed as collapsible.
The five elements that must never be re-indented
Inside <pre> and <textarea>, whitespace is
content. Every space, every newline and every tab is rendered exactly as written,
because those elements carry white-space: pre from the browser's own
stylesheet. Add two spaces of indentation to the lines of a code block and you have
edited the code sample on the page. Add a newline after
<textarea> and you have changed the default value of a form
field. This is the single most common bug in HTML beautifiers, and it is silent:
the markup is still valid, the page still loads, only the content shifted.
<script> and <style> are a different kind of
hazard. Their content is not HTML at all — it is raw text, and the parser is
required to read it as text until the matching closing tag. That means
if (a < b) inside a script is a comparison, not the start of a
<b> element. Formatters that tokenize the whole file with one
regular expression get this wrong and start "closing" tags that never opened, which
corrupts everything after that point in the document.
Re-indenting script bodies is safe more often than not, so this tool does it — but
only when the code contains no backtick. Inside a template literal, leading
whitespace is part of the string, so shifting indentation changes what the script
outputs. When a backtick shows up, the script body is emitted exactly as it came in.
Preferring "unchanged and slightly ugly" over "pretty and possibly wrong" is the
trade this whole tool is built on. CSS gets the same treatment;
<code> is left byte-identical too, since it almost always lives
inside a <pre> anyway.
Void tags, optional closing tags, and a tree that quietly drifts
Indentation depth comes from a stack, and the stack only works if the parser knows
which tags actually open a level. Fourteen HTML elements are void:
br, img, input, meta,
link, hr, source and friends. They have no
closing tag and no children. Push a <br> onto the stack and every
line after it is indented one level too deep, forever, because the pop never comes.
You can spot this failure instantly in bad formatters: the output staircases off to
the right.
The mirror-image problem is optional closing tags. </li>,
</p>, </td>, </tr>,
</option> and several others may legally be omitted, and real
templates omit them constantly. A parser that demands them will see
<li>one<li>two</ul> as an unclosed list item and
either bail out or nest the second item inside the first. This tool implements the
implied-end-tag rules: a new <li> closes the open one, a
<tr> closes the open cell, <tbody> closes
<thead>, and a block-level start tag closes an open
<p>. Tag names are also compared case-insensitively but printed
with their original case, so SVG's <linearGradient> and
viewBox survive — lowercasing those breaks the graphic.
When the structure genuinely cannot be resolved — a real unclosed
<div>, a closing tag with no opening, an unterminated comment or
quote — the tool stops and gives you your input back with the reason. That refusal
is a feature. The alternative is a document that looks formatted and has silently
lost a branch of its tree.
Formatted HTML and valid HTML are different things
People arrive at a formatter expecting it to fix their markup, and it will not,
because the two jobs have almost nothing in common. Formatting is a whitespace
operation: same tree in, same tree out, different line breaks. Validation asks
whether your document obeys the specification — whether ids are unique, whether a
<div> is allowed inside a <p> (it is not),
whether <img> has alt, whether the attributes you
used exist.
Beautifully indented HTML can fail validation on twenty counts, and gnarly minified HTML can pass cleanly. The one signal a formatter does give you is structural: if this tool refuses your file, something in the tag nesting is genuinely wrong and a validator will confirm it. For everything else, run the W3C Nu validator — it answers a question no formatter is qualified to answer.
When minifying pays, and when gzip already won
Minified HTML strips the whitespace between tags, collapses runs of spaces inside text to one, and optionally drops comments. The catch is that every one of those savings is exactly what a compression algorithm is best at. Repeated indentation is the most compressible byte pattern in existence, so gzip and Brotli have usually already removed it before it reaches the network.
| Situation | Does minifying help? |
|---|---|
| Static page served with gzip or Brotli | Barely — typically a few percent on top of compression |
| HTML stored in a database column or JSON field | Yes — no compression layer, and you pay per row |
| Email templates | Yes — Gmail clips messages over roughly 102 kB |
| HTML embedded as a string inside JavaScript | Yes — it lands in your JS bundle at full size |
| A page you still have to debug in production | No — you are trading readability for bytes you already had |
Comments are the interesting half of minification. Dropping them is usually free,
with one exception this tool hard-codes: conditional comments
(<!--[if lt IE 9]>) are logic, not annotation. They are kept even
when comment removal is on, because deleting them changes behaviour in the one
browser that reads them. Build-tool directives that live in comments deserve the
same caution, which is why the checkbox exists.
Editor formatter or online formatter?
If you are working inside a project, your editor wins and it is not close. Prettier or the built-in formatter in VS Code, WebStorm or Zed runs on save, applies the same rules to every file, and can be enforced in CI so nobody's setup drifts. Formatting should be a property of the repository, not something a person remembers to do.
An online formatter earns its place in the situations where the editor is not involved: a chunk of HTML pasted out of a Slack thread, the response body from DevTools, a template pulled from a CMS field, a snippet from a colleague on a machine where you cannot install anything, or a quick check on a phone. It is also the fastest way to see the structure of markup you did not write — one paste, one click, and the nesting is legible. The moment that snippet becomes a file in a repository, hand it back to the editor.
What happens to your file
Nothing leaves the tab. The tool is a static page: the parser, the printer, the
minifier and the syntax highlighter are JavaScript that downloads once and then runs
locally. Choosing a file uses FileReader, which reads from your disk
directly; there is no upload, no temporary storage and no request carrying your
markup. Load the page, disconnect, and it still formats.
That matters more for HTML than people assume. Real markup carries session tokens in
hidden inputs, API keys in inline scripts, customer names in email templates,
internal hostnames in src attributes, and unreleased copy in comments.
Pasting any of that into a formatter that round-trips through a server is a
disclosure, however well-meaning the server. There is no size cap, no account and no
rate limit here, because there is nothing on the other end to protect.
Frequently asked questions
Is my HTML uploaded anywhere?
No. The parser, the printer and the minifier are plain JavaScript that ships with the page and runs in your tab. Opening a file uses the browser's own FileReader, which reads from disk without a network request. You can put the page in airplane mode and it keeps working — that is the honest test, and most online formatters fail it because they POST your markup to a backend.
Will formatting change how my page renders?
That is the one thing it is built not to do. Content inside <pre>, <textarea>, <code>, <script> and <style> is copied byte for byte, and a line break is only ever inserted where whitespace already existed or at the edge of a block-level box, where browsers discard it. The single assumption that can bite you is CSS: if you set white-space: pre or display: inline on an element whose tag name says otherwise, no name-based formatter can know.
Why did it refuse to format my file and return it unchanged?
Because it hit something it could not read without guessing — an unclosed tag, a stray closing tag, a comment with no terminator, or a quote that never closes. Guessing is how formatters silently corrupt documents. The message names the tag it choked on; fix that one spot and the rest formats normally.
Does it fix invalid HTML?
No, and no formatter should. Formatting rearranges whitespace; validation is a separate judgement about whether your document obeys the spec. A file can be perfectly indented and still nest a <div> inside a <p>, duplicate an id, or leave an image without alt text. Run the W3C validator for that.
How much does minifying actually save?
On HTML alone, usually 10-25% of the raw bytes. Over the wire the number is far smaller, because gzip and Brotli already compress repeated whitespace to almost nothing — expect low single digits on top of compression. The real wins from minification are in email templates, inline HTML strings inside JavaScript, and anything stored in a database row where no compression layer exists.