HTML Formatter

Paste HTML that arrived minified, generated, or indented by three different people, and get it back with its structure visible: one block element per line, children indented, short elements kept whole. Formatting HTML is not as harmless as formatting JSON, because some whitespace in HTML is visible on the page. This formatter only adds or removes whitespace where a browser ignores it, and after every run it re-reads its own output to confirm the document is otherwise the same.

Text is only ever wrapped at a place where it already had a space. The markup is not sent anywhere and no script in it is run: it is read as text, in your browser.

Nothing formatted yet.
Paste HTML above and press Format.

What the formatter does to a page

<ul class="nav"><li><a href="/">Home</a></li><li><a href="/docs">Docs</a> <span class="new">new</span></li></ul><p>Read the
<a href="/guide">guide</a>, then <strong>try it</strong>.</p><table><tr><td>Total<td>£99.00</table>

<ul class="nav">
  <li><a href="/">Home</a></li>
  <li><a href="/docs">Docs</a> <span class="new">new</span></li>
</ul>
<p>Read the <a href="/guide">guide</a>, then <strong>try it</strong>.</p>
<table>
  <tr>
    <td>Total
    <td>£99.00
</table>

The layout follows a few rules, applied the same way every time:

  • Block elements start a line — div, p, ul, li, table, section, headings and the like — and their children are indented one level.
  • Inline content stays together. Text, links, strong, span, code, images and form controls flow along a line as they do on the page.
  • A short element stays on one line, as <li><a href="/">Home</a></li> does above. An element opens up over several lines only when it holds other blocks or is too long for the chosen width.
  • Long text wraps at the width you pick, and only at an existing space.
  • head, body and html are always opened up, and a <br> ends its line.
  • Tags are tidied, not rewritten. Runs of spaces and line breaks between attributes become one space. Attribute values, quoting, tag case and attribute order are left alone.
  • Nothing is added. The table above has no </td> or </tr>, which HTML allows, and the formatter does not supply them. It moves whitespace and nothing else.

The whitespace that matters, and why most formatters get it wrong

Between two block elements, whitespace does nothing: a browser lays out </p><p> and the same two tags on separate lines identically. Between inline elements it is different. A line break in the source is whitespace, and whitespace between two pieces of inline content is drawn as a space.

<a href="/a">One</a><a href="/b">Two</a>      renders as   OneTwo

<a href="/a">One</a>
<a href="/b">Two</a>                             renders as   One Two

A formatter that puts every tag on its own line therefore changes the page: gaps appear between buttons, between an icon and its label, between a word and the punctuation after a link. This one tracks, for every pair of neighbours, whether there was whitespace between them in your source:

  • Where there was whitespace, it may become a space or a line break, which are the same thing to a browser.
  • Where there was none, the two stay touching on the same line, however long that line gets.
  • Next to a block element, a line break is always safe and is always used.

Unknown and custom elements such as <my-card> are treated as inline, because that is how a browser treats them until a stylesheet says otherwise. Only the space, tab and line-break characters count as whitespace; a no-break space, typed directly or as &nbsp;, is content and is never touched.

The limit of this: CSS

The formatter knows HTML's defaults, not your stylesheet. If CSS turns a div into display: inline-block, or sets white-space: pre on an element that is not a pre, then whitespace the formatter considers safe may show. Grids of inline-block items with deliberately no whitespace between them are the usual case. If the layout shifts after formatting, that is where to look.

What is left exactly as it was

ContentWhat happens to it
<pre>, <textarea>Copied character for character. Every space and line break inside is content.
<script>, <style>The block is moved to the right indentation with its own relative indentation kept. The code is not reformatted.
A script holding a template literal, or of a type that is not JavaScript or JSONCopied character for character, since its leading spaces may be part of a string or a template.
Comments, including conditional commentsKept whole, in place.
Attribute valuesNever altered, including line breaks inside them.
Inline SVGIndented element by element; text inside <text> is kept together.
Template syntax such as {{ name }} or <% code %>Treated as text and left as written.

To format the JavaScript, CSS or JSON inside a page, format it on its own: the JSON formatter handles a JSON-LD block, and for XML documents such as feeds, sitemaps and SOAP messages, where every tag must be closed, use the XML formatter, which also checks that the document is well formed.

Problems it points out

Indentation is only as good as the nesting it is drawn from, so the formatter reports what makes the nesting doubtful:

  • UNCLOSED — an element such as <div> or <span> that is opened and never closed, with the line it opens on. Everything after it is indented one level too deep, which is usually how you notice.
  • STRAY — a closing tag with no matching opening tag.
  • NOTE — <div/> or <span/>. In HTML the slash is ignored on anything but a void element such as <br> or <img>, so the element stays open, which is rarely what the author meant. Self-closing is honoured inside SVG and on custom or component tags such as <Card />.

Elements whose closing tag is optional in HTML — li, p, td, tr, option, dt, dd and a few others — are closed where a browser would close them and are not reported. Broken markup is still formatted, and no tag is dropped or invented in the process.

This is not a validator. It does not know which attributes exist, which elements may contain which, or whether an id is unique. It answers one question: do the tags pair up?

Minifying, and where HTML turns up in API work

Minify applies the same rules in reverse: every space and line break a browser would ignore is removed and a run of whitespace that is visible becomes a single space. Comments and <pre> are kept as they are, and script and style blocks keep their code line for line, losing only indentation and blank lines. It is a safe minification, not an aggressive one. It does not strip comments, shorten attributes or drop optional tags, so the saving is smaller than a build tool's and the result is certain to render the same.

The common reason to format HTML while working on an API is that you received some when you expected JSON. A 502 or 503 from a proxy, a login page behind a 302, or a framework's error page behind a 500 all arrive as one long line of markup. Fetch the response with the API tester, check its Content-Type, and paste the body here to read what the server was trying to say. For HTML that has to travel inside JSON or an attribute, the HTML encoder escapes it.

Frequently asked questions

How do I format HTML online?

Paste the markup into the box above and press Format. Each block element is put on its own line with its children indented, inline text and links are kept together, and the result appears below ready to copy.

Does formatting HTML change how the page looks?

It can, with a careless formatter, because a line break between two inline elements is drawn as a space. This tool adds a line break only next to a block element or where the source already had whitespace, so the default rendering is unchanged. A stylesheet that changes how elements are displayed can still make added whitespace visible.

What is the difference between formatting and minifying HTML?

Formatting adds line breaks and indentation so that people can read the structure. Minifying removes every space and line break the browser would ignore, to make the file smaller. Both leave the rendered page the same.

Does it format the JavaScript and CSS inside the page?

It moves each script and style block to the right indentation and keeps the block's own relative indentation, but it does not reformat the code inside. A script that contains a template literal is copied exactly, since its line indentation can be part of a string.

Why does the formatter say a tag is unclosed?

An element was opened and no matching closing tag was found before its parent ended. The message gives the line of the opening tag. Elements whose closing tag is optional in HTML, such as li, p and td, are not reported.

Is <div /> valid in HTML?

It is accepted, but it does not do what it appears to. In HTML the trailing slash is ignored on any element that is not a void element, so the div stays open and the content after it becomes its child. Write an explicit closing tag instead.

Is my HTML uploaded or run?

No. The markup is read as text by JavaScript on this page. Nothing is sent to a server, and scripts, images and links inside the markup are never loaded or executed.

Related tools and reference