fast-xml-parser review
fast-xml-parser 5.11.0 reads a complete XML string or Uint8Array into JavaScript objects, checks XML syntax, and can serialize objects back to XML. Parser switches control attributes, scalar conversion, namespaces, entity expansion, child order, unpaired tags, stop nodes, and source positions. The current release adds endIndex to captured node metadata and prevents an unmatched closing tag from crashing the parser. It is pure JavaScript with ESM and CommonJS entry points, although a full browser import added 23.9 KB gzipped in our build.
fast-xml-parser 5.11.0 installed in 1.1 seconds and used 3 MB across 8 packages in our sandbox, but its complete browser import cost 23.9 KB gzipped. Pick it for complete XML payloads that need configurable object output; choose SAX events for streams and a dedicated validator for schema rules.
We installed it
| Install | ✓ · 1.1s | 8 packages on disk · 3 MB |
| Import | ✓ | ESM import works · require() works · ESM package with exports map |
| Browser | 23.9 KB | gzipped (75.4 KB minified), bundled with esbuild |
| Types | ✓ | TypeScript types bundled |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does fast-xml-parser install cleanly?
Yes. In a fresh container with an empty cache, npm install fast-xml-parser finished in 1 seconds, leaving 8 packages and 3 MB on disk. npm audit reported no known vulnerabilities.
How much does fast-xml-parser add to a browser bundle?
23.9 KB gzipped (75.4 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.
Does fast-xml-parser work with both ESM and CommonJS?
Yes. Both import 'fast-xml-parser' and require('fast-xml-parser') worked in Node 22 in our run. The package is published as ESM with an exports map.
Does fast-xml-parser include TypeScript types?
Yes, type declarations ship inside the package, so no @types install is needed.
fast-xml-parser or saxes: which should you use?
saxes: Use it when XML arrives in chunks and a SAX parser can process versioned events without holding the whole document. fast-xml-parser 5.11.0 installed in 1.1 seconds and used 3 MB across 8 packages in our sandbox, but its complete browser import cost 23.9 KB gzipped.
When should you not use fast-xml-parser?
XML arrives incrementally or may exceed memory; parse() consumes a complete string or Uint8Array instead of exposing SAX-style writes
Use it if
- A complete XML payload must become ordinary JavaScript properties, and version 5 options cover its attribute and namespace conventions
- Downstream code needs selected elements forced into arrays even when an input document contains only one occurrence
- Mixed content must retain exact child order through preserveOrder for later inspection or rebuilding
- Editor or diagnostic code needs startIndex and endIndex metadata from fast-xml-parser 5.11.0
- XML arrives incrementally or may exceed memory; parse() consumes a complete string or Uint8Array instead of exposing SAX-style writes
- You need XSD, DTD business rules, or application validation; the built-in check is syntactic and XMLValidator is deprecated
- New code primarily emits XML; XMLBuilder is deprecated in v5 and the project points new work to fast-xml-builder
- The parsed values will be inserted into a web page without escaping; accepting HTML-like tags does not sanitize scripts or markup
- You need the v6 interface to be settled; the README labels that line experimental and says its final features may differ
Setup reality
We installed fast-xml-parser 5.11.0 in 1.1 seconds on a fresh Node 22 sandbox. It produced 8 installed packages totaling 3 MB, and npm audit found 0 known vulnerabilities. The package itself declared 6 direct dependencies, 0 peers, and 1,412 KB unpacked. Its TypeScript declarations were included. Our How we test process also confirmed that both ESM import and require() worked through the exports map.
The complete browser import measured 75.4 KB minified and 23.9 KB gzipped with esbuild. That is acceptable for some client tools, but expensive if a page only reads one small feed. No credentials or config files are involved. Most integration bugs come from defaults: attributes are ignored, tag text may become numbers or booleans, and a repeated tag changes from a scalar to an array when its count moves from 1 to 2.
Set ignoreAttributes, parseTagValue, parseAttributeValue, and isArray deliberately at the parser boundary. With untrusted XML, processEntities: false avoids substituting declared entities, but it does not make the resulting strings safe HTML. Syntax validation is separate from parsing, and schema or business-rule checks require another layer. Version 5 deprecates its bundled XMLValidator in favor of fast-xml-validator.
parse() holds the input and result in memory, so a 100 MB file claim in the README is not a sizing promise for your container. preserveOrder creates a node-oriented representation instead of the usual property shape. captureMetaData in 5.11.0 can attach startIndex and endIndex through a symbol, but scalar values and arrays do not carry that metadata. XMLBuilder remains callable in v5, though its deprecation makes a new parser-and-builder abstraction worth isolating.
Patterns
Read a complete XML string parse-document
import { XMLParser } from 'fast-xml-parser';
const parser = new XMLParser();
const doc = parser.parse('<order><id>42</id></order>');
console.log(doc.order.id);Version 5 converts eligible tag text, so this id becomes the number 42 unless parseTagValue is disabled.
Expose element attributes retain-attributes
const parser = new XMLParser({
ignoreAttributes: false,
attributeNamePrefix: '@_',
});
const doc = parser.parse('<book id="b1">XML</book>');Attributes are omitted by default. A prefix separates an attribute from a child tag with the same name.
Prevent scalar coercion keep-text-as-string
const parser = new XMLParser({
parseTagValue: false,
parseAttributeValue: false,
trimValues: false,
});
const doc = parser.parse('<item code="0012"> 0012 </item>');These 3 switches preserve leading zeros in text and attributes, plus the spaces around the element text.
Return selected paths as arrays force-list-shape
const parser = new XMLParser({
isArray(name, path) {
return path === 'feed.entry';
},
});
const feed = parser.parse('<feed><entry>one</entry></feed>');Without isArray, 1 entry is a scalar and multiple entries form an array. Fix the shape where consumers expect a list.
Report malformed XML reject-bad-syntax
import { XMLValidator } from 'fast-xml-parser';
const result = XMLValidator.validate('<root><item></root>');
if (result !== true) console.error(result.err.code, result.err.line, result.err.col);XMLValidator still exists in v5 but is deprecated. Evaluate fast-xml-validator for new validation code.
Leave entity references unexpanded disable-entity-processing
const parser = new XMLParser({
processEntities: false,
ignoreAttributes: false,
});
const doc = parser.parse(remoteXml);This limits entity substitution for remote input. It does not escape parsed strings before HTML rendering.
Keep mixed content in sequence preserve-child-order
const parser = new XMLParser({ preserveOrder: true });
const nodes = parser.parse('<p>Hello <b>there</b> again</p>');preserveOrder returns a different node structure. Use matching options when rebuilding the same document.
Stop parsing inside script nodes handle-html-like-tags
const parser = new XMLParser({
stopNodes: ['..script'],
unpairedTags: ['br', 'hr'],
});
const doc = parser.parse(htmlLikeText);The ..script path matches at any depth. Supporting unpaired tags still does not turn the parser into an HTML sanitizer.
Locate an object node in the source capture-node-offsets
const parser = new XMLParser({ ignoreAttributes: false, captureMetaData: true });
const input = '<root><item id="a"/></root>';
const doc = parser.parse(input);
const meta = doc.root.item[XMLParser.getMetaDataSymbol()];
console.log(input.slice(meta.startIndex, meta.endIndex));Version 5.11.0 adds endIndex. Scalar and array results do not receive the metadata symbol.
Write an object as XML serialize-object
import { XMLBuilder } from 'fast-xml-parser';
const builder = new XMLBuilder({ ignoreAttributes: false, attributeNamePrefix: '@_', format: true });
const xml = builder.build({ order: { '@_id': '42', item: 'paper' } });XMLBuilder works in v5 but is deprecated. The project directs new generation code to fast-xml-builder.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| saxes | npm | Use it when XML arrives in chunks and a SAX parser can process versioned events without holding the whole document |
| xml2js | npm | Use it when an older Node codebase already depends on xml2js callbacks and output conventions |
| xml-js | npm | Use it when its compact and non-compact representations match an existing XML conversion layer |
More utils guides
lru-cache · type-fest · ajv · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

