Free online XML tree viewer
XML viewer that keeps the namespaces straight
Free, no account, nothing to install: paste a document of up to 5 MB and this XML viewer turns it into a foldable element tree, each row carrying its attributes, its text and the size of the branch underneath. Selecting a node gives you its XPath ready to copy, the namespace URI its prefix resolves to, and a table of every attribute in full. Filtering runs across names, attribute values and text at once, so a single entry inside a thirty-thousand-line feed is two keystrokes away rather than a scroll.
- 100% free
- No signup
- Up to 5 MB
- XPath on every node
- Namespaces resolved
Nothing open yet. Paste some markup or press Ctrl+V.
Drop a file on the box above, or press Ctrl+V while this page has focus.
This pane reads a document; it does not rewrite one. format-xml.com is the tool we keep for reshaping the markup itself.
How to view an XML file as a tree
Three steps from a wall of markup to the one element you came for.
Open the document
Paste the markup, drag a file onto the box or pick one with Choose a file; the clipboard shortcut works with nothing in particular selected, Ctrl+V or ⌘V. RSS and Atom feeds, SVG, WSDL, XSD, KML and property lists all open, up to 5 MB. As soon as it parses, the markup pane folds away and the root and its children appear unfolded.
Walk the branch you want
Each row shows the element name, its first three attributes with their values, and the text inside it; a folded row also reports how many children it has and how many elements sit in the branch below. Triangles open one level at a time, Unfold everything opens the lot for a small document, and Fold to the root puts you back at the top.
Take the XPath, or filter for what you are chasing
Selecting a row shows its full XPath — /rss/channel/item[2]/dc:creator, with a positional predicate only where siblings share a name — plus a table of every attribute and the element's text. Prefixed elements also offer the local-name() form, which matches without a namespace binding. Typing in the filter box narrows the tree to matching elements and the ancestors needed to reach them.
Technical specifications
| Documents it opens | Any well-formed XML: .xml, .xsd, .xsl, .svg, .rss, .atom, .kml, .gpx, .wsdl, .plist and plain .txt |
|---|---|
| Size ceiling | 5 MB (5,242,880 bytes) and 100,000 elements in the model, whichever comes first |
| Rows drawn at once | 2,000 maximum, however many branches are unfolded; the root and its immediate children are open on load |
| Shown per element | Qualified name, the first three attributes inline with the rest counted, text content, child count and the size of the branch below it |
| Path notation | Absolute XPath with a positional predicate only where siblings share a name — /rss/channel/item[2]/dc:creator — plus a local-name() form for namespaced elements |
| Namespaces | Every xmlns binding in the document is listed; the selected element reports the URI it resolves to and whether it arrived by prefix or by default |
| Filter | Element names, attribute names, attribute values and text, in one pass over the parsed tree; matching branches are opened and their ancestors kept for context |
| Cost and processing | Free, no account, no limit on documents; parsing and rendering both happen in this browser tab |
Frequently asked questions
Why does my XPath //item find nothing when the element is clearly there?
Almost always because the document declares a default namespace and your expression does not account for it. Once an ancestor carries xmlns="http://www.w3.org/2005/Atom", every element inside it belongs to that namespace, and in XPath 1.0 an unprefixed name means the no-namespace name — so //entry cannot match the Atom entry no matter how it is spelled. There are two ways out: bind a prefix in your XPath engine and write //atom:entry, or sidestep bindings entirely with //*[local-name()='entry']. This viewer hands you the second form on any element that has a namespace, because it is the one that works in a shell one-liner.
Why are the blank lines between my elements missing from the tree?
Because they are whitespace-only text nodes, and showing them would double the length of the tree without adding anything. Every newline and run of indentation between two tags is a real node in the DOM — a pretty-printed 200-element document typically holds around 200 of them — which is why walking childNodes in code returns text where you expected the next element. The count is printed under the tree so you know how many were set aside, and any text node with something in it is shown against its element.
What is a CDATA section, and why is the markup inside it not part of the tree?
A CDATA section is an escape hatch that tells the parser to treat everything up to ]]> as character data rather than markup. It is how an RSS feed carries a whole HTML fragment inside <content:encoded> without escaping every angle bracket. To the parser the result is text, not structure, so this viewer shows it as the element's text with a CDATA badge instead of building child nodes from it. If you need the markup inside as a tree, copy it out and open it as its own document.
Why do some elements show a prefix such as dc: and others do not?
The prefix is only a local nickname for a namespace URI, and it is the URI that identifies the element. A feed that writes xmlns:dc="http://purl.org/dc/elements/1.1/" could rename dc: to anything and mean exactly the same thing, while an element with no prefix belongs either to the default namespace or to no namespace at all. The panel under the tree spells out which URI the selected element resolves to, and the footer lists every binding in the document, because two files that look different often differ in nothing but their choice of prefixes.
Can I open a large XML file here without the tab locking up?
Up to 5 MB, yes, because the tree draws only what is unfolded. The model is built once and holds up to 100,000 elements; the pane then renders at most 2,000 rows however many branches are open, so scrolling stays smooth on a feed with tens of thousands of entries. Filtering is the faster way through a big file: it narrows the tree to matching elements and their ancestors in a single pass rather than making you fold your way down.
Can I change a value here and save the file?
No — this is a reading tool, and nothing you do to the tree alters the document. That is deliberate: exploring a payload and editing it want different interfaces, and a viewer that quietly re-serialises what it parsed will normalise your quoting, your empty-element style and your attribute order along the way. For reshaping the markup itself use format-xml.com, our dedicated XML tool, and for turning the document into something a script can loop over, the XML to JSON converter.
The tree does not appear at all — what went wrong?
The document did not parse, and XML parsers stop at the first fault rather than recovering. The bar under the tree shows the browser's message and the line it stopped on; the XML validator on this site gives you the column, the source line with a caret under it and the rule that was broken. Unescaped ampersands, a mismatched closing tag and two root elements account for most of it.
About reading XML structurally
A document is not really a text file with tags in it — it is a tree of typed nodes, and the reason a viewer helps is that the two shapes disagree in ways that are invisible in the source. Elements have children; attributes do not, so an attribute can never carry structure and never repeats within a tag. The indentation between two tags is not decoration but a text node in its own right. A comment is a node, a processing instruction is a node, and a CDATA section is a way of writing text that happens to look like markup. Walking childNodes in your own code and finding whitespace where the next element should be is the moment most people meet this distinction; the tree here draws elements, shows text against the element that holds it, and reports the whitespace nodes as a count rather than as rows.
Namespaces are the other thing worth seeing plainly. Namespaces in XML 1.0 defines an element’s real identity as a pair — the namespace URI and the local name — with the prefix as nothing more than a local nickname declared by an xmlns: attribute. Two feeds that spell Dublin Core as dc:creator and dcterms:creator are saying the same thing if both prefixes point at the same URI, and two that use the same prefix for different URIs are not. Because a default xmlns= applies to every descendant element and to no attribute, half a document can quietly belong to a namespace its author never thinks about.
That is also the source of the most frequently asked XML question there is: why //entry matches nothing in an Atom feed. XPath 1.0 has no notion of a default namespace, so an unprefixed step means the no-namespace name and cannot reach an element that belongs to one; you either bind a prefix in the host language or write //*[local-name()='entry']. Both forms are offered here for exactly that reason. When the document will not open at all, the well-formedness checker gives you the column and the rule; when the point of reading it was to loop over it, converting it to JSON is usually the next move; and when the markup itself needs reshaping, that lives on format-xml.com, our separate tool for the job.
What happens to the document you open
Documents opened here stay on your own machine: the parser already inside your browser does the work, and no request is ever made carrying the markup. A configuration export or a licence file can therefore be examined without it reaching a server, and the tree keeps working after the network drops, because there was never a server in the loop.