2026-10-22 · 8 min read
- xml
- formatting
- tooling
The XML page parses with DOMParser in application/xml mode and serializes with XMLSerializer. Format inserts a newline only where a > is immediately followed by a <, indents some of the resulting lines by two spaces, and adds a final newline. Minify returns the serializer string and does not add that newline.
Whitespace that trims to nothing never reaches the parser, so the output is empty. Whitespace around a document is discarded by the parse. The document <root>x</root> with spaces before and after it prints as <root>x</root> plus a newline in format mode.
Let only some opening tags add a level
A line gains one indent level when it starts with < and a letter, the character just before > is not a slash, and the line does not already contain </. <root> matches. <a> does not, because there is no character available for that test before the >. <a><b><c>d</c></b></a> formats as five unindented lines. The third line is <c>d</c>, and the last two are the closing tags.
<root><item a="1">hi</item></root> indents the item by two spaces. <r> and a self-closing child do not indent, and the serializer also removes the space in <self-closing />. Empty elements collapse: <root></root> and <b></b> become <root/> and <b/>. A namespaced child that has a bound prefix stays one line when it contains its own text, and it is indented when its parent matched the rule.
- Minify first if you need a stable format.
- Expect one-letter tags to stay at column 0.
- Expect empty elements to use />.
- Do not format a pretty document a second time.
Treat minify as serialize, not as trim
Text nodes survive minify. A pretty-printed root still contains its newline and its two-space line when you press Minify. Formatting that result adds another level: the item line is printed at four spaces, and </root> returns to column 0. Diff will show a real whitespace change.
Interior spaces stay. <root> followed by two spaces, hi, and two spaces keeps them. A newline inside an item is indented with the following text, so line2</item> can sit four spaces in while the closing tag of the item remains on that same line. Comments are preserved and do not open a level. <?xml version="1.0"?> stays, on its own line when the next tag was adjacent. A processing instruction is kept. Single-quoted attributes become double-quoted. a > b in text becomes a > b. CDATA is kept, including a < inside it.
Read the parsererror text with its missing space
A parse failure is copied as Error: plus the browser's parsererror text. In Chromium that text begins This page contains the following errors: and continues directly into error on line 1, with no space added by the page. not xml reports characters outside the root element. An unclosed root reports that the stream ended inside the element. <root></Root> reports Unexpected closing tag. a=1 without quotes reports Unexpected token. A repeated attribute reports that it is redefined. <n:name> without a declaration reports that the prefix is unbound.
An HTML document parsed this way is still XML. <html><body><p>hi</p></body></html> is indented because html and body match the rule, and the one-letter p tag stays on one line with its text. It is not passed through an HTML parser.
Format from one minified source
Keep the original bytes, minify only when you want the serializer's spelling, and format once for a review. Diff the formatted text against a previous format if the indent moved. A one-letter tag that looks unformatted is following the rule, not dropping a level you had typed.
Use the XML tool for the print, diff for the second pass, the HTML entities tool when the payload is escaped text rather than a document, and the regex tester to find a one-letter tag or a </ that shares a line with text. Say whether the input was already indented before you treat a four-space line as a schema change.