An HTML formatter takes an HTML string and re-emits it with consistent indentation and line breaks. The tool above runs the formatter in your browser; nothing leaves your machine. The interesting question is what the formatter has to be careful not to change.
What gets indented
The formatter’s main job: nest each element under its parent and indent appropriately.
Input: <article><h1>Title</h1><p>Body text</p></article>
Output:
<article>
<h1>Title</h1>
<p>Body text</p>
</article>
Default indent is 2 spaces (sometimes 4, sometimes tabs). Most formatters offer the choice.
What’s whitespace-sensitive
HTML has elements where whitespace matters and where it doesn’t:
| Element | Whitespace handling |
|---|---|
Block elements (<div>, <p>, <article>, <section>) | Whitespace between children is collapsed and rendered minimally |
Inline elements (<span>, <a>, <em>, <strong>) | Whitespace between/around is rendered as spaces |
<pre> | Whitespace is preserved exactly |
<textarea> | Whitespace is preserved exactly |
<code> (inside <pre>) | Whitespace is preserved |
<script>, <style> | Bytes preserved; not parsed as HTML |
A naïve formatter that adds line breaks anywhere can change behavior:
Input: <p>Hello <a href="...">world</a>!</p>
If the formatter naively breaks lines:
<p>
Hello
<a href="...">world</a>!
</p>
The rendered text now has extra spaces around “world” — a subtle difference that affects spacing. A correct HTML formatter knows that <a> is inline and either:
- Keeps the inline element on the same line as the surrounding text.
- Breaks but uses careful whitespace that preserves rendering.
The tool above keeps inline elements on the same line by default.
Void elements
HTML has elements that don’t have closing tags: <img>, <br>, <hr>, <meta>, <link>, <input>. These are void elements — self-closing by definition.
<img src="..."> ← HTML5
<img src="..." /> ← XHTML / JSX
<br> ← HTML5
<br/> ← XHTML / JSX
A formatter has to decide which form to emit. Most modern formatters default to HTML5 (no slash). XHTML-strict formatters add the slash.
Attributes
<input
type="email"
name="email"
required
placeholder="you@example.com"
>
For elements with many attributes, formatters break each attribute onto its own line. The threshold (how many attributes before breaking) varies; common defaults are 3–4 attributes or a line-length limit (80 or 100 characters).
Boolean attributes (required, disabled, checked) don’t need values in HTML5 — required is equivalent to required="required". Formatters typically preserve whichever form the source used.
Comments
<!-- A regular comment -->
<!--[if IE]>
<p>Internet Explorer-specific markup</p>
<![endif]-->
The first form is just a comment. The second is an “Internet Explorer conditional comment” — IE used to interpret these specially. Modern browsers ignore them; the formatter should preserve their structure exactly because some legacy systems still use them.
Doctype
<!DOCTYPE html> ← HTML5
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "..."> ← HTML 4
Always at the top, no indentation. Formatters preserve whatever doctype the source had.
What the formatter doesn’t do
- Doesn’t add missing closing tags. Browsers tolerate sloppy HTML (unclosed
<li>,<td>,<p>); a formatter usually preserves the source’s structure rather than “fixing” it. - Doesn’t validate. Format-and-emit is a different operation from validate. An HTML linter (
HTMLHint,vnu.jar) does validation; the formatter does not. - Doesn’t reorder attributes. Source order is preserved.
- Doesn’t remove or merge classes.
<div class="a a">stays as-is. - Doesn’t optimize. A formatter is the inverse of a minifier; it doesn’t strip anything.
When formatting hurts
- Auto-generated HTML. Build-tool output (React-rendered HTML, framework-rendered SSR output) usually doesn’t need formatting and may break if formatted (Vue’s whitespace handling is opinionated).
- Email HTML. Many email clients are picky about whitespace inside specific elements. Format with care; test in actual email clients.
- HTML inside JavaScript template literals. Some formatters leave these alone; some try to format them and break things.
Idempotence
Format a file once; format the result again — the second pass should produce identical output. If the formatter is non-idempotent, it has a bug. Easy to test: pipe the source through twice and diff the results.
Common pitfalls
- Formatting
<pre>content. Whitespace inside<pre>is significant. A formatter that re-indents inside<pre>changes rendered output. Most formatters know to leave<pre>alone. - Formatting JSX/Vue/Svelte/Astro syntax. These look like HTML but aren’t strictly. Use a formatter that understands the specific syntax (Prettier with the right plugin) rather than a pure-HTML one.
- Stripping intentional
. Some formatters “normalize” whitespace and turn into spaces, breaking layout. Most don’t, but check after formatting if mattered to the design. - Re-adding closing tags. Some formatters insert
</li>and</p>even when the spec allows them to be omitted. Mostly harmless; sometimes breaks template logic. - Formatting an unclosed tag. A missing
</div>confuses the parser; output is unpredictable. Fix the markup first, format second.
For the inverse direction, see HTML minifier. For other format conversions, see JSON formatter, CSS formatter, XML formatter.