Free online HTML formatter and beautifier
HTML formatter that renders the same afterwards
Paste markup and get it back indented, free and without an account, with one guarantee that most beautifiers cannot make: the page still looks exactly as it did. A line break is only ever introduced where whitespace already existed or next to a block element where whitespace cannot render, so inline tags stay welded to their neighbours and no new gaps appear between them. pre and textarea come out character for character, attribute lists wrap past a column limit you choose, and embedded script, style and JSON-LD blocks are re-indented by the printers those languages have here. Documents up to 5 MB.
- 100% free
- No signup
- Up to 5 MB
- pre kept verbatim
- Script and style re-indented
The indented document appears here.
Paste markup, drop a file, or press Ctrl+V anywhere on this page.
How to format HTML
Three steps, and the rendered page is untouched at the end of them.
Paste the markup
Drop it in the left panel, hit ⌘V or Ctrl+V anywhere on the page, or open an .html file. A whole document works, and so does a fragment: a table ripped out of a CMS field, a component template, one <head> you are trying to audit. Only text/plain is read from the clipboard, because the clipboard's HTML flavour is the browser's own serialisation rather than the markup you copied.
Set the indent and the attribute limit
Choose 2 spaces, 4 spaces or a tab, then say how wide a start tag may get before its attributes go one per line — 80, 100, 120 columns, or never. Leave “Re-indent script and style” ticked to have embedded JavaScript, CSS and JSON-LD laid out by the printers those languages have on this site, or untick it to leave those blocks exactly as written.
Read the report, then take the file
Underneath the panes you get the element count, the attribute count, how many regions were left untouched and how deep the tree goes, plus a line for anything unusual: a stray closing tag, an element the next tag closed for it, a script the JavaScript reader would not accept. Copy takes the document; Download saves it as .html.
Technical specifications
| Line-break rule | A break is only introduced where the source already had whitespace, or beside a block-level element where whitespace cannot render. Two inline elements written with nothing between them stay welded together |
|---|---|
| Element tables | 40 inline elements kept in the text flow, 14 void elements written without an end tag, 14 elements whose end tag may be omitted and is written back in, 26 block elements that close an open paragraph |
| Copied character for character | <pre> and <textarea> contents, comments including multi-line ones, CDATA sections, and <?php … ?> and <% … %> fragments |
| Attribute handling | Each attribute copied whole — case, quoting and order untouched. A start tag longer than the wrap width (80, 100 or 120 columns) puts one attribute per line with the closing > on its own line; a tag with fewer than two attributes is never split |
| Embedded blocks | <script> goes to the JavaScript printer, <style> to the CSS printer, and type="application/ld+json" or "importmap" to the JSON parser. A block any of them refuses is copied out unchanged and named in the report |
| Never changed | Tag and attribute case (SVG's viewBox and linearGradient depend on it), entity references, attribute quoting style, and the order of anything |
| Not supported | Template control flow ({{#if}}, {% block %}) has no tree to indent against; text content is never re-wrapped to a column width; markup is not validated and invalid nesting is reproduced, not corrected |
| Throughput and price | 2,408 elements in 43.6 KB laid out in 5 ms; 5 MB per document; free, no signup, and the markup is parsed in your browser rather than uploaded |
Frequently asked questions
Why did my <pre> block get reindented by other tools?
Because most formatters treat a document as a tree of tags and forget that inside <pre> the whitespace is the content. Adding two spaces to every line of a code sample shifts the code sample; wrapping a long line inside <pre> puts a line break into the text a reader sees. This formatter never touches the inside of a <pre> or a <textarea>: the start tag, everything between it and the end tag, and the end tag are copied out as one run of characters, and the counter tells you how many such regions it left alone.
Will indenting my HTML add spaces between my inline elements?
Not here, and this is the whole reason the rest of the rules exist. In normal white-space mode a browser collapses a run of whitespace to a single space and renders it, so putting a line break between </span> and <span> where the source had none turns ab into a b — a visible change, and a well-known cause of gaps between inline-block cards and stray spaces in a breadcrumb trail. The rule followed here is that a line break may only be introduced where whitespace already existed, or beside a block-level element where whitespace cannot render at all. Forty elements are treated as inline, img, button, label and svg among them.
Why does the output have closing tags I never wrote?
Because HTML lets you leave some of them out, and the browser inserts them anyway — so the formatter writes down the tree the browser will build rather than the text you typed. Fourteen elements have optional end tags: a second <li> closes the first, a <tr> closes the open <td>, and a <p> is closed by any of 26 block elements that cannot legally sit inside it. Writing them out is not a change to the page; it is the same DOM with the shape made visible. If you prefer them absent, delete them afterwards — the parse is identical either way.
What happens to the JavaScript and CSS inside my page?
They are handed to the JavaScript and CSS printers this site already runs, then indented one level in from their tag. A <script type="application/ld+json"> block goes to the JSON parser instead, so your structured data comes back as a readable tree. Anything those readers refuse — a template language in a script tag, a half-finished function, a CSS block with an unbalanced brace — is copied out exactly as written and named in the report underneath. A block is never partly formatted.
Does it change my tag or attribute case?
No, and that is a correctness decision rather than a preference. HTML tag names are case-insensitive, but SVG and MathML live in the same document and are not: lowercasing <linearGradient> or the viewBox attribute breaks the graphic, and React's markup carries camelCase attributes for the same reason. Nothing is lowercased, no quotes are added or removed around attribute values, and no attribute is reordered — the text of each attribute is copied across whole.
Can it handle Vue, Handlebars, PHP or Blade templates?
Partly, and it says which part. A <?php … ?> or <% … %> fragment is copied out verbatim as a single unit, so the code inside it is never re-spaced, and moustache expressions in text are ordinary text. What the reader does not do is understand template control flow: a {{#if}} block that opens in one element and closes in another has no tree for the indenter to follow, so the surrounding markup is indented by the real tags and the template directives ride along where they sit. Attribute syntax such as :prop, @click and v-if is preserved exactly, since attributes are copied rather than parsed.
What does it do with a tag I never closed?
It closes it at the end of the document and tells you so, which is what a browser does. An unclosed <div> means everything after it is nested inside it, so the output is deeply indented and the note under the tool names the element — that indentation is the bug becoming visible rather than the formatter misbehaving. A closing tag with nothing to close is left exactly where it was found and reported, because deleting markup is not a formatter's job.
About indenting HTML safely
Indenting HTML is not a text problem, and that is what catches tools out. In every other language on this site whitespace between tokens is free; in HTML it is sometimes free and sometimes a character the reader sees. Between two block elements it collapses to nothing and can be moved at will. Inside a paragraph — or between two inline-block elements, which is the case that bites — a run of whitespace collapses to exactly one space and is rendered, which is why <span>a</span><span>b</span> and the same markup with a newline between the tags produce different pages. Any formatter that indents by walking the tree and printing each child on its own line will introduce those spaces, and the damage shows up later as a mysterious four-pixel gap between two cards.
The other half of the problem is that HTML is forgiving in ways that make the text and the tree diverge. Fourteen elements have end tags you may omit, so <li>one<li>two is two siblings rather than a nested pair, and a paragraph is closed by any of 26 block elements that cannot live inside it — which is how <p>text<div> silently ends the paragraph before the div begins. Four elements hold character data rather than markup, so a < inside a script is not a tag, and the script ends at the first </script> even if that string is sitting inside a JavaScript literal. Getting those rules right is most of what separates an indenter from a corrupter.
One consequence is worth planning around: a formatted document is bigger, because every indent is bytes on the wire. Format while you are reading and working, and let the build output be compact — the CSS minifier and JavaScript minifier handle the assets, and gzip erases most of the difference in the markup itself. If what you are pasting is a page you want the text out of rather than the tags, that is a different job with its own site.
Where your markup is processed
The reader, the printer and the two embedded-language printers all run inside this tab, so your document goes nowhere and no request is made for anything it references. Nothing in your markup is fetched or rendered either — an <img> in the text you paste stays text, and a script stays text.