The Complete Guide to HTML Formatting
HTML formatting is one of those housekeeping tasks that separates maintainable projects from ones where nobody wants to touch the templates. This guide covers why it matters, how Prettier handles it, and the edge cases you'll run into.
Why HTML formatting matters
Inconsistent indentation makes HTML harder to read than it needs to be. When tags are nested five levels deep with mixed tabs and spaces, spotting an unclosed <div> becomes a game of counting brackets. Consistent formatting gives you:
- Visual nesting — indentation reveals parent-child relationships at a glance
- Easier debugging — unclosed tags and mismatched elements jump out when the structure is clean
- Team consistency — no more style arguments in code review; the formatter decides
- Cleaner diffs — when everyone formats the same way, git diffs show only meaningful changes
If you're working solo, formatting is nice. On a team, it's non-negotiable.
Beautify vs minify
Beautifying adds whitespace, line breaks, and indentation to make HTML human-readable. Minifying strips all unnecessary whitespace to reduce file size.
<!-- Beautified -->
<div class="card">
<h2>Title</h2>
<p>Content goes here.</p>
</div>
<!-- Minified -->
<div class="card"><h2>Title</h2><p>Content goes here.</p></div>
In production, minification saves bandwidth — but how much? HTML compresses extremely well with gzip and brotli. A 50 KB HTML file might save 2-3 KB after minification, but gzip already compresses the beautified version to nearly the same size as the minified+gzipped version. The gains are real but marginal compared to JS/CSS minification.
Practical advice: serve beautified HTML in development (you need to debug it). In production, minify if you care about every byte, but don't lose sleep over it. Your build tool probably handles this anyway.
How Prettier formats HTML
Prettier doesn't do regex-based find-and-replace. It parses your HTML into an AST (abstract syntax tree), then reprints it from scratch using consistent rules. This means:
- Attribute formatting — long attribute lists get broken across multiple lines with consistent indentation
- Self-closing tags — handled according to HTML5 rules (
<br>not<br />unless you configure XHTML mode) - Embedded code —
<style>and<script>blocks are formatted using Prettier's CSS and JS formatters respectively - Whitespace-sensitive elements —
<pre>,<code>,<textarea>, and inline elements preserve their whitespace because changing it would alter rendering
The AST approach means Prettier produces identical output regardless of input formatting. Tabs, spaces, no spaces — the result is always the same.
HTML validation vs formatting
Formatting and validation solve different problems. A formatter fixes whitespace and style. A validator checks structural correctness.
A formatter will happily indent this invalid HTML:
<div>
<p>
<div>Nested block inside inline — invalid</div>
</p>
</div>
It looks clean, but <div> inside <p> is invalid HTML. The browser will auto-close the <p> before the inner <div>, producing a different DOM than the source suggests.
For validation, use the W3C Markup Validation Service or html-validate in your CI pipeline.
HTML5 quirks worth knowing
HTML5 has rules that surprise developers coming from XHTML:
- Optional closing tags —
<li>,<p>,<td>,<th>,<tr>,<head>,<body>can all be implicitly closed.<ul><li>One<li>Two</ul>is valid HTML5. - Void elements —
<br>,<img>,<input>,<hr>,<meta>,<link>never have closing tags.<br></br>is wrong. - Boolean attributes —
disabled,checked,readonlydon't need values. Write<input disabled>not<input disabled="true">. The presence of the attribute is what matters.
Prettier respects these rules by default. If you're writing XHTML (rare in 2025), you'll need to configure the parser accordingly.
Troubleshooting
The formatter changes whitespace around my inline elements — Prettier is whitespace-sensitive for inline elements like <span>, <a>, and <strong>, but it may reflow lines in ways that add or remove spaces between them. If a space between two inline elements matters for rendering, Prettier preserves it. If you see elements collapsing together, check that the original source had the space in the right place.
Embedded JS or CSS inside <script>/<style> tags isn't formatted correctly — Prettier formats embedded code blocks using its JS and CSS formatters, but only when it can detect the language. If you're using a type attribute like type="text/template" or type="application/ld+json", Prettier may skip formatting that block. For JSON-LD, ensure the content is valid JSON.
Self-closing tags differ between HTML5 and XHTML — In HTML5, void elements don't use self-closing syntax: <br> not <br />. If your output shows <br />, check whether the parser is set to html or xhtml. Prettier defaults to HTML5 rules. If you need XHTML output (e.g., for JSX compatibility or XML-based systems), configure the parser option.
The formatter breaks my template syntax (Handlebars, EJS, Jinja) — Prettier's HTML parser doesn't understand template delimiters like {{ }}, <% %>, or {% %}. It may split them across lines or mangle the output. For template files, use a Prettier plugin specific to your templating language (e.g., prettier-plugin-jinja-template) or exclude those files from HTML formatting.