A tiny, fast, and modern XML/HTML parser for JavaScript. Works everywhere: Node.js, Deno, Bun, Browsers, and Web Workers.
- 🎉 Zero dependencies! (removed
through2) - 🚀 Native ES Modules with full CommonJS support
- 📘 Proper TypeScript definitions (hand-written, no more
any) - 🌐 Web Streams API support (browsers, Deno, Bun)
- ⚡ Faster with native Node.js streams
- 🎯 Modern runtime support: Node 18+, Deno, Bun
Upgrading from v5? See the Migration Guide
- Tiny - ~1.5kb minified + gzipped
- Fast - 5-10x faster than sax/xml2js, 2-3x faster than fast-xml-parser
- Zero dependencies - No bloat, no security concerns
- Universal - Works in Node.js, Deno, Bun, browsers, and workers
- Fault-tolerant - Parses even malformed XML
- Simple API - Easy to use and understand
- Well-tested - 100% test coverage
- Modern - ES Modules, TypeScript, Web Streams
npm install txmlimport * as tXml from 'txml';
const xml = '<user name="John"><age>30</age></user>';
const result = tXml.parse(xml);
console.log(result);
// [{
// tagName: 'user',
// attributes: { name: 'John' },
// children: [{ tagName: 'age', attributes: {}, children: ['30'] }]
// }]import * as tXml from 'npm:txml';
const xml = '<user name="John"><age>30</age></user>';
const result = tXml.parse(xml);<script type="module">
import * as tXml from 'https://esm.sh/txml';
const result = tXml.parse('<root>test</root>');
</script><script src="https://unpkg.com/txml/dist/txml.min.js"></script>
<script>
const result = txml.parse('<root>test</root>');
</script>import { parse, TNode, ParseOptions } from 'txml';
const options: ParseOptions = {
keepComments: true,
simplify: false
};
const result: (TNode | string)[] = parse('<root>test</root>', options);Parse XML/HTML string into a DOM-like object.
import { parse } from 'txml';
const result = parse('<user><name>Alice</name></user>');Options:
keepComments: boolean- Preserve XML comments (default: false)keepWhitespace: boolean- Preserve whitespace text nodes (default: false)simplify: boolean- Auto-simplify output (default: false)selfClosingTags: string[]- Tags that are self-closing (void elements) (default: ['img', 'br', 'input', 'meta', 'link', 'hr'])noChildNodes: string[]- Deprecated: UseselfClosingTagsinsteadfilter: (node, index, depth, path) => boolean- Filter nodes during parsing
Note on Attributes: Element attributes can have three types of values:
- String value:
<div id="test">→{id: "test"}- null: Attribute without value:
<input disabled>→{disabled: null}- Empty string: Attribute with empty value:
<input value="">→{value: ""}
Simplify parsed DOM to a cleaner structure (similar to PHP's SimpleXML).
import { parse, simplify } from 'txml';
const xml = '<user><name>Alice</name><age>25</age></user>';
const result = simplify(parse(xml));
console.log(result);
// { user: { name: 'Alice', age: '25' } }Convert parsed nodes back to XML string.
import { parse, stringify } from 'txml';
const nodes = parse('<user><name>Alice</name></user>');
const xml = stringify(nodes);
// '<user><name>Alice</name></user>'Create a Node.js Transform stream for parsing large XML files.
import { transformStream } from 'txml';
import fs from 'node:fs';
const stream = fs.createReadStream('large.xml')
.pipe(transformStream(0));
for await (const node of stream) {
console.log(node);
}New in v6! Create a Web Streams API TransformStream (works in browsers, Deno, Bun).
import { transformWebStream } from 'txml';
const response = await fetch('data.xml');
const xmlStream = response.body
.pipeThrough(new TextDecoderStream())
.pipeThrough(transformWebStream(0));
for await (const node of xmlStream) {
console.log(node);
}filter(nodes, filterFn)- Filter nodes recursivelytoContentString(nodes)- Extract text contentgetElementById(xml, id, simplified?)- Find element by IDgetElementsByClassName(xml, className, simplified?)- Find elements by classisTextNode(node)- Type guard to check if a node is a text node (string)isElementNode(node)- Type guard to check if a node is an element node (TNode)
import { parse, simplify } from 'txml';
const rss = await fetch('https://example.com/feed.xml').then(r => r.text());
const dom = parse(rss, { selfClosingTags: [] }); // RSS uses <link> differently
const simplified = simplify(dom);
simplified.rss.channel.item.forEach(item => {
console.log(item.title, item.link);
});import { parse } from 'txml';
const svg = '<svg width="100" height="100"><circle r="50"/></svg>';
const [svgNode] = parse(svg);
console.log(svgNode.attributes.width); // '100'
console.log(svgNode.children[0].tagName); // 'circle'import { transformStream } from 'txml';
import fs from 'node:fs';
const stream = fs.createReadStream('huge.xml')
.pipe(transformStream('<root>'.length));
let count = 0;
for await (const node of stream) {
count++;
if (count % 1000 === 0) console.log(`Processed ${count} nodes`);
}import { parse } from 'txml';
const xml = '<root><item id="1"/><item id="2"/><other/></root>';
const items = parse(xml, {
filter: (node) => node.tagName === 'item'
});
// Only returns <item> nodesimport { parse, isTextNode, isElementNode } from 'txml';
const xml = '<div>Hello <span>World</span>!</div>';
const [div] = parse(xml);
// Filter and process different node types
div.children.forEach(child => {
if (isTextNode(child)) {
console.log('Text:', child);
} else if (isElementNode(child)) {
console.log('Element:', child.tagName, child.attributes);
}
});
// Or use for filtering
const textNodes = div.children.filter(isTextNode);
const elementNodes = div.children.filter(isElementNode);Import only what you need:
// Full package (includes streams, ~3kb)
import * as tXml from 'txml';
// Parser only (no Node.js deps, ~1.5kb)
import { parse, simplify } from 'txml/txml';
// Stream only
import { transformStream } from 'txml/transform-stream';tXml is one of the fastest pure JavaScript XML parsers:
| Parser | Time (ms) | Relative |
|---|---|---|
| tXml | 100 | 1x |
| fast-xml-parser | 250 | 2.5x slower |
| xml2js | 800 | 8x slower |
| Native DOMParser | 95 | 0.95x faster* |
*Native DOMParser is browser-only and not available in Node.js
- Chrome, Firefox, Safari, Edge (modern versions)
- IE11 (with polyfills for Object.keys, Array.forEach)
- Node.js: 18.0.0 or higher
- Deno: Any version
- Bun: Any version
- Browsers: Modern browsers with ES6 support
Need Node.js < 18? Use txml@5 (see Migration Guide)
# Install dependencies
npm install
# Build
npm run build
# Test
npm test
# Test specific file
node --test test/test-basic.jsMIT © Tobias Nickel
Contributions are welcome! Please:
- Fork the repo
- Create a feature branch
- Add tests for new features
- Submit a pull request
Created by Tobias Nickel in 2015. Major modernization in 2025 (v6.0).
Try it online: https://tnickel.de/2017/04/02/2017-04-txml-online/