HTML Formatter Online

Paste HTML and get it back indented, highlighted and easy to read, or minified to one line. It is for anyone reading a minified page or View Source, tidying markup copied from DevTools, a CMS or an email builder, or shrinking a snippet before it ships: choose 2 spaces, 4 spaces or tabs, and long start tags get one attribute per line.

Everything runs in your browser, so your HTML is not uploaded, and it is never rendered or run. HTML is forgiving, so DevPrix formats it anyway and lists what a browser would have to repair, such as an unclosed or stray tag, a <p> that a <div> closes, or a duplicate attribute, each with its line and column.

InputPaste, type or drop an .html file
Output
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Release notes</title>
    <link rel="stylesheet" href="/css/site.css">
    <style>main{max-width:40rem;margin:auto}</style>
  </head>
  <body>
    <header class="site-header">
      <nav aria-label="Main">
        <ul>
          <li><a href="/">Home</a></li>
          <li><a href="/docs/">Docs</a></li>
          <li><a href="/blog/" aria-current="page">Blog</a></li>
        </ul>
      </nav>
    </header>
    <main>
      <h1>Release notes <small>v2.1</small></h1>
      <p>
        Builds are <strong>faster</strong> now: the cache is shared between <em>all</em> CI runs.<br>
        Upgrade with:
      </p>
      <pre><code>npm install example-cli@2.1
npx example-cli build</code></pre>
      <!-- newsletter signup -->
      <form action="/subscribe" method="post">
        <label for="email">Email</label> <input id="email" name="email" type="email" required> <button type="submit">Subscribe</button>
      </form>
    </main>
    <script src="/js/site.js" defer></script>
  </body>
</html>
No problems found31 elementsDepth 7Standards mode948 B → 1.1 KBProcessed locally

How to use it

  1. Paste HTML into Input, or open or drop an .html file; it is formatted as you type.
  2. Choose Formatted or Minified and their options, and read the Warnings list for tags a browser would repair.
  3. Press Format (⌘↵) to apply the result to your input, then copy, download or send it to another tool.

Related tools

CSS Formatter & MinifierFormatXML Formatter & ValidatorFormatHTML Entity EncoderWeb & APIMarkdown to HTMLConvert

Next: compare two versions in the Text Diff Checker, escape the markup for documentation with the HTML Entity Encoder, or check a page's social preview tags in the OG Preview.

What this tool does

The tool reads your markup the way browsers do. It follows the HTML tokenizer for tags, quoted, unquoted and valueless attributes, comments, the DOCTYPE and raw text, and a simplified version of the browser's tree builder for void elements such as <br> and <img>, optional end tags such as </li> and </p>, elements that end others (a <div> closes an open <p>), tables, <select>, <template>, and SVG or MathML, where <path /> really is self-closing. Formatting only changes whitespace: no tag is added or removed, and attributes keep their order, quotes and case. Because whitespace is rendered in some places and not in others, each gap is judged by the default CSS display of the elements around it. Next to a block element such as <div>, <p>, <li> or <td> whitespace is not shown, so blocks go on their own lines. Between inline content (text, <a>, <span>, <strong>, <img>, <button>) a space is shown, so a run of spaces and newlines becomes one space, but a space is never removed and never added where there was none. Text inside <pre>, <textarea>, <script>, <style> and <title>, inside SVG <text>, and inside any element whose style attribute sets white-space: pre, is copied exactly. Minified mode removes the whitespace that is not shown and every <!-- comment --> except conditional comments such as <!--[if IE]>; it can also drop quotes that are not needed and shorten disabled="disabled" to disabled. When tags overlap, as in <b><i>x</b></i>, or content sits directly inside a <table>, browsers rebuild elements and a new line break could change the result, so in that case the tool only re-indents whitespace that is already there. The tool cannot see your stylesheets: if CSS makes list items inline-block, or sets white-space: pre through a class, check those parts of the output.

Example input and output

Minified markup, formatted with 2 spaces
Input
<nav class="site"><ul><li><a href="/">Home</a></li><li><a href="/docs/">Docs</a></li></ul></nav><main><h1>Release notes <small>v2.1</small></h1><p>Builds are <strong>faster</strong> now.<br>Upgrade with:</p><pre><code>npm install example-cli@2.1</code></pre></main>
Output
<nav class="site">
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/docs/">Docs</a></li>
  </ul>
</nav>
<main>
  <h1>Release notes <small>v2.1</small></h1>
  <p>
    Builds are <strong>faster</strong> now.<br>
    Upgrade with:
  </p>
  <pre><code>npm install example-cli@2.1</code></pre>
</main>

Inline content such as Release notes <small>v2.1</small> stays on one line with its spaces, and a line break follows each <br>.

A list inside a paragraph
Input
<p>Our plans:
  <ul>
    <li>Free</li>
    <li>Pro</li>
  </ul>
</p>
Output
<p>Our plans:
<ul>
  <li>Free</li>
  <li>Pro</li>
</ul>
</p>

Line 2, column 3: <ul> closes the <p> opened on line 1
Line 6, column 1: </p> without an open <p>: browsers insert an empty paragraph here

A paragraph cannot contain a list, so browsers end the <p> before the <ul>, and the last </p> becomes an extra empty paragraph. The indentation shows the structure browsers build. Use a <div> instead of the <p>.

Minify, keeping inline spaces and preformatted text
Input
<article>
  <!-- intro -->
  <h2>Install</h2>
  <p>Run <code>npm  install</code> and <em>restart</em>
     the server.</p>
  <pre>
npm install
npm start</pre>
</article>
Output
<article><h2>Install</h2><p>Run <code>npm install</code> and <em>restart</em> the server.</p><pre>
npm install
npm start</pre></article>

The comment and the indentation between blocks are removed. The spaces around <code> and <em> stay (as one space each), and the <pre> block is kept exactly, newlines included.

Common errors and fixes

Unclosed <span>: it is still open at </div>
An element was not closed before its parent ended, so the browser closes it there for you. That is often harmless, but it can be a missing </span> in the wrong place, which makes the styling spill further than intended. Add the end tag where the element should end.
</span> is ignored: <div> (line 1) is still open inside <span>
Browsers ignore an end tag when a block element opened inside it is still open, so the <span> stays open and swallows what follows. Close the inner <div> first, or avoid putting a <div> inside a <span>.
<ul> closes the <p> opened on line 1
A paragraph can hold only text and inline elements. A <div>, <ul>, <table> or heading ends the open <p>, and a later </p> then creates an extra empty paragraph. Use a <div> as the container, or close the paragraph before the block.
<script/> is not self-closing in HTML
In HTML a "/" before > is ignored on elements that are not void, so <script src="app.js"/> stays open and treats the rest of the page as script; <div/> stays open too. Write <script src="app.js"></script>. Only void elements (<br>, <img>, <input> …) and SVG or MathML elements may end with />.
<p> directly inside <table> is moved before the table
Content in a table that is not inside a cell is "foster-parented": browsers move it in front of the table. Put it inside a <td> or <th>, or move it outside the table.
</b> closes <b> while <i> (line 1) is still open
The tags overlap: <b><i>x</b></i>. Browsers repair it by splitting and re-nesting elements, which may not be what you meant. Close the inner element first: <b><i>x</i></b>.
Duplicate attribute class on <div>
An attribute can appear only once per element. Browsers keep the first value and silently ignore the rest, so class="a" class="b" gives just class="a". Merge them: class="a b".

Common use cases

  • Reading minified HTML from a build, a CMS export or View Source
  • Finding the unclosed or stray tag that breaks a layout
  • Tidying markup copied from DevTools, an email builder or a design tool
  • Minifying an HTML snippet or email template without touching pre, script or style
  • Checking a hand-edited page before a deploy

Privacy and security notes

  • Your HTML is parsed and formatted by JavaScript in this browser tab. It is not sent to DevPrix or stored on a server.
  • The markup is never rendered or run: the output is shown as highlighted text, so scripts, event handlers such as onerror, iframes and forms in it do nothing, and nothing it refers to (stylesheets, images, scripts, links) is fetched.
  • Share copies a link that carries your input after the # in the URL. Browsers do not send that part to servers, but anyone you give the link to can read it, so do not share links that contain credentials or personal data.

Frequently asked questions

Will formatting change how my page looks?

It should not. Only whitespace changes, and only where it is not shown with the default CSS: next to block elements, between table cells and inside the head. Between inline elements a space stays a space (one, instead of several) and is never added where there was none, and <pre>, <textarea>, <script> and <style> are copied exactly. The tool cannot see your stylesheets, so if CSS gives elements a different display, for example inline-block list items whose gaps depend on the spaces between them, or sets white-space: pre through a class, check those parts. Inline style="display: …" and style="white-space: pre" are taken into account.

Why does it not add the missing </li>, </p> or </td>?

HTML allows those end tags to be left out: a new <li> ends the previous one, and a <div> ends an open <p>. The formatter never adds or removes tags, it only moves whitespace, so the output means exactly what the input meant. It indents the result the way browsers nest it and warns where an end tag is missing that the browser would have to guess, such as an unclosed <div> or <span>.

Why is a tag indented where I did not expect it?

The indentation follows the tree browsers build, not the tags as written. For example, <p>Intro<div>x</div></p> becomes a paragraph followed by a <div> at the same level, and a stray </p> that adds an empty paragraph; <span><div>x</span></div> keeps the <span> open, because browsers ignore that </span>. The Warnings list explains each case with its line and column.

What does minify remove?

Whitespace that is not shown, and comments, except conditional comments such as <!--[if IE]> and processing instructions such as <?php … ?>, which are usually server-side code. Turn on Keep comments to keep them all. Unquote attributes drops quotes that are not needed (class="btn" becomes class=btn), and Short booleans writes disabled="disabled" or disabled="" as disabled; both are off by default. Optional end tags are kept, and the code inside <script> and <style> is not minified: use the CSS Formatter for styles.

Can I format Vue, Svelte, JSX or template files?

The tool reads everything as HTML. Template syntax such as {{ name }}, {% if %}, <%= x %> and <?php … ?> is kept as text, so Handlebars, Liquid, Nunjucks, EJS and PHP templates format well. Framework components are different: in HTML <MyCard /> is an opening tag, so what follows would be nested inside it, and JSX is JavaScript, not HTML. Use Prettier or your framework's formatter for those.