HTML Formatting: Clean Markup That's Easy to Read and Maintain
HTML is forgiving by design. Browsers render badly indented, improperly nested, unclosed tags without complaint. But the humans reading and maintaining that markup are not so forgiving. Clean HTML formatting reduces bugs, improves collaboration, and makes accessibility issues easier to spot.
Indentation: the foundation
Use consistent indentation -- 2 spaces is the most common convention in the web ecosystem. Every nested element gets one additional level:
<main>
<section class="hero">
<h1>Welcome</h1>
<p>This is properly indented HTML.</p>
<div class="actions">
<button type="button">Get Started</button>
<a href="/learn-more">Learn More</a>
</div>
</section>
</main>
Compare with the same markup unformatted:
<main><section class="hero"><h1>Welcome</h1><p>This is properly indented HTML.</p><div class="actions"><button type="button">Get Started</button><a href="/learn-more">Learn More</a></div></section></main>
The second version is functionally identical but nearly impossible to scan for structure. You cannot tell at a glance where the section ends or how many children the actions div has.
When to break indentation rules
Inline elements within a line of text should stay inline:
<!-- Good: inline elements stay on the line -->
<p>Click <a href="/settings">here</a> to update your <strong>settings</strong>.</p>
<!-- Unnecessary: breaking inline elements to separate lines -->
<p>
Click
<a href="/settings">here</a>
to update your
<strong>settings</strong>.
</p>
The second version is harder to read because it fragments a natural sentence across five lines.
Self-closing tags
In HTML5, void elements (elements that cannot have children) do not require a closing slash:
<!-- HTML5: no closing slash needed -->
<img src="photo.jpg" alt="A photo">
<input type="text" name="email">
<br>
<hr>
<meta charset="utf-8">
<link rel="stylesheet" href="styles.css">
<!-- XHTML style: closing slash (valid but not required in HTML5) -->
<img src="photo.jpg" alt="A photo" />
<input type="text" name="email" />
Both styles are valid HTML5. The important thing is consistency within a project. If you use React/JSX, you must use self-closing syntax (<img />) because JSX requires it. For plain HTML templates, the slash-free style is more common in modern codebases.
Attribute ordering
A consistent attribute order makes elements scannable. Here is a widely used convention:
<input
id="email"
class="input-field"
type="email"
name="email"
placeholder="your@email.com"
required
aria-label="Email address"
data-validate="email"
>
The recommended order:
id-- unique identifier, most important for linkingclass-- styling hook, the most frequently changed attributetype/name/value-- element-specific attributessrc/href/action-- resource referencesalt/title/placeholder-- human-readable labels- Boolean attributes --
required,disabled,readonly,checked - *
aria-** -- accessibility attributes - *
data-** -- custom data attributes
This ordering puts the most commonly referenced attributes first and groups related ones together.
Multi-line attributes
When an element has many attributes, put each on its own line:
<!-- Few attributes: single line is fine -->
<button class="btn-primary" type="submit">Save</button>
<!-- Many attributes: one per line -->
<input
id="search"
class="search-input"
type="search"
name="query"
placeholder="Search..."
autocomplete="off"
aria-label="Search the site"
data-testid="search-input"
>
A good rule of thumb: if the element would exceed 80-100 characters on a single line, break it to multiple lines. The closing > can go on the last attribute line or on its own line -- pick one style and be consistent.
Boolean attributes
HTML boolean attributes are true when present and false when absent. Do not assign them a value:
<!-- Good -->
<input type="text" required disabled>
<details open>
<script defer src="app.js"></script>
<!-- Unnecessary (but valid) -->
<input type="text" required="required" disabled="disabled">
<input type="text" required="" disabled="">
The value-free style is cleaner and universally understood.
Formatting with Prettier
Prettier handles HTML formatting with sensible defaults:
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"singleQuote": false,
"htmlWhitespaceSensitivity": "css",
"bracketSameLine": false
}
The htmlWhitespaceSensitivity option is important. It controls how Prettier handles whitespace around inline elements. The "css" setting (default) respects the CSS display property -- inline elements preserve surrounding whitespace, block elements do not.
# Format all HTML files
npx prettier --write "**/*.html"
# Check without modifying
npx prettier --check "**/*.html"
Prettier and template languages
Prettier supports HTML within Vue (.vue), Svelte (.svelte), and Angular templates. For other template languages (EJS, Handlebars, Jinja), Prettier may not format correctly. In those cases, use editor extensions or language-specific formatters.
Clean markup and accessibility
Well-formatted HTML makes accessibility issues visible. Consider this unformatted markup:
<div onclick="navigate('/about')"><div><span>About Us</span></div></div>
When formatted properly, the problems become obvious:
<div onclick="navigate('/about')">
<div>
<span>About Us</span>
</div>
</div>
Now you can clearly see: a div with a click handler instead of a proper link, unnecessary wrapper divs, and a span where heading text should be. The correct version:
<a href="/about">About Us</a>
Clean formatting does not fix accessibility, but it makes problems visible to developers during code review.
Common formatting mistakes
Inconsistent quotes. HTML allows both " and ' for attribute values. Pick double quotes (the HTML standard convention) and enforce it with Prettier or a linter.
Mixing indentation. Tabs in one file, 2 spaces in another, 4 spaces in a third. This is especially common in projects with multiple contributors. Add an .editorconfig file to standardize:
[*.html]
indent_style = space
indent_size = 2
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
Deep nesting without reason. If your HTML is indented 8 or more levels deep, the structure likely needs simplification. Extract components, flatten the DOM, or reconsider the layout.
Commented-out code blocks. Large blocks of commented HTML obscure the actual markup. Remove them -- version control preserves the history if you need it back.
Summary
HTML formatting is about human readability, not browser correctness. Consistent indentation, logical attribute ordering, proper use of self-closing tags, and automated formatting with Prettier keep your markup maintainable. Clean markup also surfaces accessibility issues that messy formatting hides.
Try our HTML Beautifier to format and clean up your HTML instantly -- right in your browser, no upload required.