DevToys Web Pro iconDevToys Web ProBlogi
Hinda meid:
Proovi brauserilaiendust:
← Back to Blog

HTML Formatter Guide: Whitespace, Void Elements, and Minification

11 min read

HTML is deceptively simple to write but surprisingly tricky to format correctly. Unlike most programming languages, whitespace in HTML is not always insignificant — collapsing a space between two inline elements can change how the page renders. Preserving the contents of a pre block is mandatory. Void elements have no closing tag at all. These constraints make an HTML formatter a more nuanced tool than a JavaScript or JSON formatter. Paste any markup into the HTML Formatter to follow along with the examples below.

This guide walks through how HTML formatters handle the tricky parts: significant versus insignificant whitespace, protected content blocks, void and self-closing elements, attribute wrapping, boolean attributes, minification trade-offs, and why auto-formatting leads to cleaner version-control diffs. We also draw the line between a formatter and a validator so you know which tool to reach for.

Significant vs Insignificant Whitespace

The core complexity of formatting HTML is that whitespace is context-dependent. The HTML specification defines a set of rules for collapsing runs of whitespace inside most elements, but inline elements and the white-space CSS property can override those rules at render time.

Block vs inline context

Inside a block-level container like div or p, indentation and newlines between child elements are collapsed to a single space or nothing at all, so a formatter can freely reindent those elements. Between two adjacent inline elements, however, a single whitespace character is rendered as a visible space glyph. Removing it changes the visual output.

<!-- Safe to reformat: block context -->
<div>
  <p>First paragraph</p>
  <p>Second paragraph</p>
</div>

<!-- Whitespace-sensitive: inline context -->
<p>Click <a href="/about">here</a> to learn more.</p>

A formatter that strips the space between the a tag and the surrounding text would remove the visual gap between "Click" and "here". Good formatters preserve at least one whitespace token between adjacent inline nodes.

The white-space CSS property

Even elements that would normally collapse whitespace can be given white-space: pre or white-space: pre-wrap via CSS, making every space and newline significant at runtime. A static formatter cannot know what CSS will be applied, so it conservatively treats pre and textarea as always significant and leaves their contents byte-identical.

Protected Content: pre, textarea, script, and style

Four element types must never have their contents reformatted by an HTML formatter:

ElementWhy content is protectedWhat a formatter does
preRenders whitespace literally; used for code samples, ASCII artPasses content through unchanged
textareaDefault value includes leading/trailing whitespace verbatimPasses content through unchanged
scriptContains JavaScript; indentation is cosmetic but mangling breaks ASIMay reindent as a block, never reformats JS content
styleContains CSS; whitespace inside selectors and values is significantMay reindent as a block, never reformats CSS content

If you need to format the JavaScript inside a script block or the CSS inside a style block, extract it into a standalone file and run the appropriate formatter on it separately. An HTML formatter's job is the markup structure, not the embedded languages.

<!-- Formatter leaves the pre content exactly as-is -->
<pre><code>function greet(name) {
    return "Hello, " + name;
}</code></pre>

<!-- Formatter may adjust the script tag's indentation
     but will not touch the JavaScript inside -->
<script>
  const API = "https://example.com/api";
  fetch(API).then(r => r.json()).then(console.log);
</script>

Void and Self-Closing Elements

HTML has a fixed set of void elements — elements that cannot have children and therefore have no closing tag. The HTML5 parser knows these by name; a closing tag is a syntax error. Common void elements include br, hr, img, input, link, and meta.

<!-- Correct HTML5: no closing tag, no self-closing slash -->
<img src="/logo.png" alt="Logo">
<input type="text" name="q">
<br>
<hr>

<!-- Also valid HTML5 (XHTML legacy): self-closing slash is ignored by HTML parser -->
<img src="/logo.png" alt="Logo" />
<input type="text" name="q" />
<br />
<hr />

A formatter normalizes the style you choose — either always strip the trailing slash or always add it — so the codebase is consistent. The HTML Formatter follows the Prettier convention of keeping the slash for void elements when the document is treated as HTML5, because many React/JSX codebases copy-paste snippets between HTML and JSX where the slash is required.

Self-closing custom elements

Custom elements (web components) are not void elements even if they happen to have no children. They always require an explicit closing tag. A formatter that converts my-icon /> to my-icon> without a closing tag would produce broken markup, so formatters leave non-void elements with an explicit closing tag regardless of whether they are currently empty.

Attribute Wrapping and Print Width

Long lists of attributes on a single element are one of the most common sources of unreadable HTML. A formatter can wrap attributes to respect a configured print width — typically 80 or 100 characters — placing each attribute on its own line and aligning the closing angle bracket.

<!-- Before formatting: all attributes on one line -->
<input type="email" id="email" name="email" placeholder="you@example.com" required autocomplete="email" class="form-control">

<!-- After formatting at print width 80: one attribute per line -->
<input
  type="email"
  id="email"
  name="email"
  placeholder="you@example.com"
  required
  autocomplete="email"
  class="form-control"
>

The multi-line form is dramatically easier to diff: adding, removing, or reordering a single attribute produces a single-line change in version control. With everything on one line, any attribute change touches the entire line, obscuring what actually changed.

Attribute quote normalization

HTML attribute values can be quoted with double quotes, single quotes, or left unquoted if they contain only certain safe characters. Formatters normalize to double quotes by default, consistent with the HTML specification's examples. An unquoted value like class=container becomes class="container".

Boolean Attributes

Boolean attributes in HTML have a special rule: their mere presence signals true; the value is irrelevant. All four of the following are equivalent and valid:

<input required>
<input required="">
<input required="required">
<input required="true">  <!-- not idiomatic, but parsed identically -->

A formatter normalizes boolean attributes to the shortest form — bare attribute name, no value — unless the target is XHTML, which requires an explicit value. The common boolean attributes you will see normalized are required, disabled, checked, readonly, multiple, selected, and autofocus.

Minification: Gains, Options, and Risks

HTML minification strips bytes from a document before it is served, reducing transfer size and potentially improving page load time. The gains depend heavily on how verbose the original markup is.

TechniqueTypical savingSafe?
Collapse whitespace between block elements5–15 %Yes for block context
Remove HTML comments1–5 %Yes (except IE conditional comments)
Remove optional closing tags1–3 %Usually yes, but test thoroughly
Collapse inline whitespace2–8 %Risky — can break inline rendering
Remove optional quotes on attributes< 1 %Risky — breaks with spaces in values

Collapse whitespace

Collapsing runs of whitespace between block-level elements is the safest and most impactful minification step. The browser already collapses them when rendering, so removing them from the source changes only the byte count, not the output. Inline whitespace collapse is far riskier — as discussed above, removing the space between adjacent inline elements changes the visual layout.

Remove comments

Regular HTML comments are purely for developers and are safe to strip in production builds. The one exception is Internet Explorer conditional comments, which are directives to the IE rendering engine. If you still target IE (unlikely in 2026), disable comment removal or handle them explicitly.

Optional closing tags

The HTML5 specification designates a handful of closing tags as optional — li, dt, dd, p, tr, td, th, and a few others. Omitting them is valid HTML, but some older parsing pipelines, screen readers, or DOM-manipulation code may misbehave. Leaving optional closing tags in place is the conservative choice; removing them in a well-tested production build is a reasonable micro-optimization.

<!-- With optional closing tags (explicit, safer) -->
<ul>
  <li>Apple</li>
  <li>Banana</li>
  <li>Cherry</li>
</ul>

<!-- Without optional closing tags (minified, technically valid) -->
<ul>
  <li>Apple
  <li>Banana
  <li>Cherry
</ul>

Combining with gzip

On a gzip- or Brotli-compressed connection, the marginal benefit of HTML minification shrinks considerably — most of the byte savings from minification are already captured by compression. The real remaining value of minification is reducing the uncompressed parse work in the browser and eliminating comment-carried information that you do not want visible in View Source.

Formatted HTML Produces Cleaner Diffs

One underappreciated benefit of consistent HTML formatting is its effect on code review. When every file is formatted to the same standard, a diff shows only the lines that actually changed. Without a formatter, a colleague who happens to use a different editor setting reindents a block while adding a single attribute, and the pull request diff is full of noise that obscures the real change.

The attribute-per-line style amplifies this benefit. Adding an aria-label attribute to a button is a single-line addition in a diff. With all attributes on one line, adding any attribute rewrites the whole line.

<!-- Before: single-line attributes -->
- <button type="button" class="btn btn-primary" disabled>Submit</button>
+ <button type="button" class="btn btn-primary" aria-label="Submit form" disabled>Submit</button>

<!-- After: one attribute per line — the diff is surgical -->
  <button
    type="button"
    class="btn btn-primary"
+   aria-label="Submit form"
    disabled
  >Submit</button>

Pair your formatter with a pre-commit hook so HTML files are always formatted before they reach the repository:

# Install Prettier with HTML support
npm install --save-dev prettier

# Format all HTML files in the project
npx prettier --write "**/*.html"

# Check formatting in CI (exit code 1 on drift)
npx prettier --check "**/*.html"
{
  "printWidth": 80,
  "tabWidth": 2,
  "htmlWhitespaceSensitivity": "css",
  "singleAttributePerLine": false,
  "bracketSameLine": false
}

The htmlWhitespaceSensitivity option in Prettier controls how aggressively whitespace is collapsed. The default value "css" respects the element's computed display role — inline elements keep their whitespace, block elements do not. Setting it to "strict" treats every element as inline-sensitive (safest), and "ignore" collapses all whitespace regardless of context (most compact, most risky).

Formatter vs Validator: Different Tools, Different Jobs

A common point of confusion is expecting a formatter to catch HTML errors. It does not — or at least that is not its job. The two tools have distinct mandates:

  • Formatter — takes valid (or near-valid) HTML and reformats its layout: indentation, line breaks, attribute wrapping, quote style. It does not enforce the HTML specification.
  • Validator — checks the document against the HTML specification and reports errors like missing alt attributes, duplicate id values, improperly nested elements, or obsolete attributes. The W3C Markup Validation Service is the canonical validator.

Some formatters parse the HTML well enough that they reject badly nested tags, but that is a side effect of needing a parse tree to format, not a deliberate validation pass. Do not rely on a formatter to catch accessibility or specification violations — use a dedicated validator or linter (such as HTMLHint or axe) for that.

After formatting, view the result in the HTML Preview tool to confirm the rendered output looks correct before committing.

Practical Workflow

The recommended workflow for HTML in a modern web project:

  1. Author — write HTML without worrying about indentation or line length.
  2. Format on save — editor integration (VS Code Prettier extension) runs the formatter every time you save. Whitespace and attribute style are normalized instantly.
  3. Preview — use HTML Preview to visually verify the formatted markup renders as expected, especially around inline elements where whitespace changes could affect layout.
  4. Pre-commit hook — lint-staged runs Prettier only on staged HTML files, keeping the hook fast.
  5. CI checkprettier --check fails the build if any unformatted HTML slips through.
  6. Production minification — a build step (webpack, Vite, or a dedicated HTML minifier) produces the minified output from the well-formatted source. The source stays readable; the build artifact is compact.

Keeping formatting and minification as separate steps is important: the source should be the formatted, readable version under version control, and the minified version should be a build artifact that is never committed.

If you work with a variety of file types — JavaScript, CSS, JSON — the Code Formatters Guide covers the shared principles behind all code formatters: parse-then-print, idempotency, and CI integration patterns that apply across languages.


Prettify or minify markup directly in your browser with the HTML Formatter — it runs locally so your code never leaves your machine. To visually verify the result, open the HTML Preview tool alongside it.