iKit
Guide · 9 min read ·

How to Format XML Online in 2026: A Developer's Guide

Format XML online without uploading anything. How browser formatters indent, why whitespace is risky, and when to reach for xmllint instead.

How to Format XML Online in 2026: A Developer's Guide

How to Format XML Online in 2026

A SOAP response comes back as one 40 KB line. A build tool prints a pom.xml fragment with no newlines. You need to read it now, and pasting customer data into a random web form is not an option. Formatting XML online is a five-second job — as long as the formatter parses in your browser and you understand what re-indenting actually does to the document.

TL;DR

  • Browser-side XML formatters parse and re-serialise locally — nothing is uploaded.
  • Indentation adds whitespace text nodes; it is a display change, not a lossless one.
  • A formatter checks well-formedness; only a schema validator checks validity.
  • xmllint --format is the CLI equivalent, with known limitations without a DTD.
  • Most "unformattable" XML is a BOM, an unescaped &, or two root elements.

How to format XML online without uploading the file

The reason people search for this is rarely curiosity about indentation. It is that the XML in question contains an order ID, a patient record, or an internal endpoint, and the obvious formatters are server round-trips.

What a browser-side formatter actually does

Every modern browser ships a conforming XML parser behind DOMParser. A client-side formatter hands your text to it, walks the resulting tree, and writes it back out with indentation. No network call is involved after the page loads:

const parser = new DOMParser();
const doc = parser.parseFromString(src, "application/xml");

// Well-formedness check
if (doc.querySelector("parsererror")) {
  throw new Error("Not well-formed XML");
}

Per MDN's DOMParser reference, when the input is not well-formed the returned document contains a <parsererror> node describing the failure rather than throwing. That node is how a browser formatter reports errors, and it is why the error text you see differs slightly between Chrome and Firefox — each engine words its own diagnostics.

Serialisation back to a string uses XMLSerializer. MDN is blunt that the round trip is not a guarantee: the resulting string is not promised to be well-formed XML, which matters when you feed it hand-built DOM fragments rather than parsed documents.

Three steps, no account

  1. Open xmlformatter.ikit.app and paste the payload.
  2. Read the indented tree; collapse the branches you do not care about.
  3. Copy the formatted output, or copy the original if you are forwarding it on.

There is no sign-up, no upload progress bar, and no file-size cap other than your own RAM. Open the network tab if you want to confirm it — after the initial asset load, the request list stays empty. That is the same architecture behind the rest of the suite, described in how iKit runs entirely in your browser.

When you should not paste at all

If the document is a signed SOAP envelope or anything carrying an XML Signature, do not reformat it and then send it onward. Canonicalisation rules decide which bytes get hashed, and casual re-indentation will break the signature. Read the formatted copy, transmit the original.

Why XML formatters disagree about whitespace

Two formatters can produce different output from the same input and both be correct. The reason is that XML, unlike JSON, has no concept of insignificant whitespace at the document level.

Whitespace between elements is character data

In <a><b/></a> there is no text node between <a> and <b/>. In the pretty-printed version there is — a newline plus two spaces. The parser reports it. A consumer that iterates childNodes rather than children will now see an extra node it did not see before.

This is why xmllint carries an unusually candid warning in its own manual: the --format option, especially in the absence of a DTD, "has never worked reliably and is fundamentally broken" — the libxml2 maintainers' own words in the xmllint documentation. Without a schema, the tool cannot know which whitespace is ignorable, so it guesses.

Mixed content is where guessing hurts

<p>Please <b>sign</b> before Friday.</p>

Indent that naively and you get:

<p>
  Please
  <b>sign</b>
  before Friday.
</p>

The rendered sentence now has different spacing. Any element that can hold both text and children is unsafe to re-indent, which is exactly what a DTD or XSD would have told the formatter if one were available.

What good formatters do about it

Behaviour Safe for reading Safe for re-sending
Indent element-only content Yes Usually
Indent mixed content Yes No
Preserve CDATA verbatim Yes Yes
Collapse attribute whitespace Yes No

The practical rule: treat formatted output as a view, not as a replacement artefact. If you need a byte-stable normalised form for comparison or signing, that is canonicalisation (C14N), a different operation with its own spec.

How to pretty print XML from the command line

The browser tool and the CLI are complements, not competitors. When the file is on a server or inside a pipeline, reach for xmllint, which ships with libxml2 and is present on most Linux distributions and on macOS.

The two flags worth memorising

# Re-indent to stdout
xmllint --format messy.xml

# Check well-formedness, print nothing
xmllint --noout suspect.xml

Indentation defaults to two spaces and is controlled by the XMLLINT_INDENT environment variable, not by a flag:

XMLLINT_INDENT="    " xmllint --format pom.xml

Exit codes make it scriptable

xmllint returns distinct codes per failure class, which is what makes it usable in CI:

Code Meaning
0 No error
2 Error in DTD
3 Validation error
4 Not well-formed or unreadable

A pre-commit hook that runs xmllint --noout over every .xml in the tree and fails on non-zero catches broken markup before review does.

Where the CLI stops being enough

xmllint prints the first error and its line number. When a 12 MB feed has an unescaped ampersand somewhere in the middle, a browser formatter that highlights the offending region in context is faster than a line number and a sed expression. Use whichever gets you to the character quicker.

How to fix "XML declaration allowed only at the start of the document"

Roughly three errors account for most of the XML developers paste into a formatter.

The invisible byte order mark

A UTF-8 BOM — bytes EF BB BF — before <?xml version="1.0"?> is the single most common cause. The prolog must be the first thing in the document, and the BOM counts as content to a strict parser. Check it before guessing:

head -c 3 feed.xml | xxd
# 00000000: efbb bf    ...

Re-save as UTF-8 without BOM and the error disappears. The same class of invisible-byte problem shows up in spreadsheets, which is why CSV files arrive garbled in Excel for almost exactly the opposite reason.

Raw ampersands and angle brackets

XML defines five predefined entities, and & is not permitted as a literal character in content or attribute values:

<!-- Broken -->
<url>https://example.com/?a=1&b=2</url>

<!-- Correct -->
<url>https://example.com/?a=1&amp;b=2</url>

Escaping rules are the same family of problem as URL and HTML encoding — three different escaping layers that developers routinely confuse. If the payload is a URL inside XML, you may be dealing with two of them at once; the URL Encoder and HTML Encoder handle the other two.

Two root elements

Concatenating two API responses produces a document with two top-level elements. The XML 1.0 specification requires exactly one root element per document — the W3C XML recommendation is the authority here. Wrap the fragments in a synthetic container before formatting:

<responses>
  <response id="1">…</response>
  <response id="2">…</response>
</responses>

Formatter vs validator: what each one actually checks

The two words get used interchangeably and they mean different things.

Well-formedness is syntax

Every start tag closed, tags properly nested, attribute values quoted, one root, reserved characters escaped, a legal encoding declaration. Any conforming parser enforces this. Fail here and no downstream tool will accept the document at all.

Validity is contract

Validity is conformance to a schema — DTD, W3C XML Schema (XSD), RelaxNG, or Schematron. It answers questions well-formedness cannot: is <shippedAt> allowed inside <order>, is @currency required, is this field a date. xmllint exposes each schema language through its own flag (--dtdvalid, --schema, --relaxng, --schematron), and returns exit code 3 rather than 4 when the failure is validity rather than syntax.

Which one you actually need

  • Debugging a 500 from a SOAP endpoint — well-formedness first; malformed markup is the usual cause.
  • Rejecting a partner's nightly feed — validity, against the agreed XSD.
  • Reading an unfamiliar config file — formatting alone; there is nothing to validate against.

A browser formatter answers the first and third instantly. For the second, keep the schema in the repository and run the check in CI where it belongs.

References

Related on iKit

Related posts