# saxen

> A tiny, super fast, namespace aware sax-style XML parser written in plain JavaScript

Latest version **11.1.1** (published 2026-07-27) · MIT license · 0 weekly downloads

## Install

```sh
npm install saxen
pnpm add saxen
yarn add saxen
bun add saxen
```

## Health

**Score 60/100 (C)** — status: active.

Positive: esm support; no vulnerabilities; recently updated; high maintenance score.

Warnings: low downloads; no types.

## Facts

| | |
|---|---|
| Version | 11.1.1 |
| Published | 2026-07-27 |
| First published | 2017-11-06 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | none |
| Module format | ESM + CommonJS |
| Node | >= 20.12 |
| Dependencies | 0 |
| Unpacked size | 156.2 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 23 |
| Author | Nico Rehwaldt |
| Maintainers | nikku |
| Keywords | xml, sax, parser, pure |

## Links

- npm: https://www.npmjs.com/package/saxen
- Repository: https://github.com/nikku/saxen
- Homepage: https://github.com/nikku/saxen#readme
- Issues: https://github.com/nikku/saxen/issues
- npm.io page: https://npm.io/package/saxen

## Alternatives

- [babylon](https://npm.io/package/babylon.md) — 5.1M weekly downloads
- [csscolorparser](https://npm.io/package/csscolorparser.md) — 3.7M weekly downloads
- [expr-eval-fork](https://npm.io/package/expr-eval-fork.md) — 1.5M weekly downloads
- [@leeoniya/ufuzzy](https://npm.io/package/@leeoniya/ufuzzy.md) — 247.7K weekly downloads
- [xml-parser](https://npm.io/package/xml-parser.md) — 78.4K weekly downloads

## Recent versions

- 11.1.1 (latest) — 2026-07-27
- 11.1.0 — 2026-07-14
- 11.0.2 — 2025-12-14
- 11.0.1 — 2025-12-14
- 11.0.0 — 2025-12-14
- 10.0.0 — 2024-03-05
- 9.0.0 — 2022-12-21
- 8.1.2 — 2020-04-08
- 8.1.1 — 2020-04-01
- 8.1.0 — 2018-10-07
- 8.0.0 — 2018-04-03
- 7.0.1 — 2018-03-26
- 7.0.0 — 2018-03-18
- 6.0.1 — 2018-03-18
- 5.7.0 — 2018-01-09
- … 24 more at https://npm.io/package/saxen/versions

## README

# `/saxen/` parser

[![CI](https://github.com/nikku/saxen/workflows/CI/badge.svg)](https://github.com/nikku/saxen/actions?query=workflow%3ACI)
[![Codecov](https://img.shields.io/codecov/c/github/nikku/saxen.svg)](https://codecov.io/gh/nikku/saxen)


A tiny, super fast, namespace aware [sax-style](https://en.wikipedia.org/wiki/Simple_API_for_XML) XML parser written in plain JavaScript.


## Features

* (optional) entity decoding and attribute parsing
* (optional) namespace aware
* element / attribute normalization in namespaced mode
* tiny (`2.6Kb` minified + gzipped)
* [pretty damn fast](https://github.com/nikku/js-sax-parser-tests)


## Usage

```javascript
import {
  Parser
} from 'saxen';

const parser = new Parser();

// enable namespace parsing: element prefixes will
// automatically adjusted to the ones configured here
// elements in other namespaces will still be processed
parser.ns({
  'http://foo': 'foo',
  'http://bar': 'bar'
});

parser.on('openTag', function(elementName, attrGetter, decodeEntities, selfClosing, getContext) {

  elementName;
  // with prefix, i.e. foo:blub

  const attrs = attrGetter();
  // { 'bar:aa': 'A', ... }
});

parser.parse('<blub xmlns="http://foo" xmlns:bar="http://bar" bar:aa="A" />');
```


## Supported Hooks

We support the following parse hooks:

* `openTag(elementName, attrGetter, decodeEntities, selfClosing, contextGetter)`
* `closeTag(elementName, decodeEntities, selfClosing, contextGetter)`
* `error(err, contextGetter)`
* `warn(warning, contextGetter)`
* `text(value, decodeEntities, contextGetter)`
* `cdata(value, contextGetter)`
* `comment(value, decodeEntities, contextGetter)`
* `attention(str, decodeEntities, contextGetter)`
* `question(str, contextGetter)`

In contrast to `error`, `warn` receives recoverable errors, such as malformed attributes.

In [proxy mode](#proxy-mode), `openTag` and `closeTag` a view of the current element replaces the raw element name. In addition element attributes are not passed as a getter to `openTag`. Instead, they get exposed via the `element.attrs`:

* `openTag(element, decodeEntities, selfClosing, contextGetter)`
* `closeTag(element, selfClosing, contextGetter)`


## Namespace Handling

In namespace mode, the parser will adjust tag and attribute namespace prefixes before
passing the elements name to `openTag` or `closeTag`. To do that, you need to
configure default prefixes for wellknown namespaces:

```javascript
parser.ns({
  'http://foo': 'foo',
  'http://bar': 'bar'
});
```

To skip the adjustment and still process namespace information:

```javascript
parser.ns();
```


## Proxy Mode

In this mode, the first argument passed to `openTag` and `closeTag` is an object that exposes more internal XML parse state. This needs to be explicity enabled by instantiating the parser with `{ proxy: true }`.

```javascript
// instantiate parser with proxy=true
const parser = new Parser({ proxy: true });

parser.ns({
  'http://foo-ns': 'foo'
});

parser.on('openTag', function(el, decodeEntities, selfClosing, getContext) {
  el.originalName; // root
  el.name; // foo:root
  el.attrs; // { 'xmlns:foo': ..., id: '1' }
  el.ns; // { xmlns: 'foo', foo: 'foo', foo$uri: 'http://foo-ns' }
});

parser.parse('<root xmlns:foo="http://foo-ns" id="1" />')
```

Proxy mode comes with a performance penelty of roughly five percent.

__Caution!__ For performance reasons the exposed element is a simple view into the current parser state. Because of that, it will change with the parser advancing and cannot be cached. If you would like to retain a persistent copy of the values, create a shallow clone:

```javascript
parser.on('openTag', function(el) {
  const copy = Object.assign({}, el);
  // copy, ready to keep around
});
```


## Streaming Mode

Instead of parsing a complete document via `parse`, you may feed the parser XML in chunks via `write` and signal the end of the stream via `end`. This allows you to process huge documents in a memory efficient, step by step manner:

```javascript
const parser = new Parser();

parser.on('openTag', function(name) { /* ... */ });

parser
  .write('<foo>')
  .write('<bar />')
  .write('</foo>')
  .end();
```

Chunks may split anywhere, even in the middle of a tag, attribute, comment or CDATA section; the parser buffers the incomplete remainder until the next `write`. Parse events are emitted as soon as the corresponding token is complete. Calling `end` flushes the stream and reports an error (via the `error` hook and as the return value) if the buffered remainder is incomplete.

The `write` / `end` pair mirrors Node's [writable stream](https://nodejs.org/api/stream.html#class-streamwritable) contract, making it straightforward to wire the parser up as a stream sink.

__Note:__ In streaming mode the parse context (`line` / `column`) is relative to the currently buffered input rather than the whole document.


## Non-Features

`/saxen/` lacks some features known in other XML parsers such as [sax-js](https://github.com/isaacs/sax-js):

* no support for parsing loose documents, such as arbitrary HTML snippets
* no support for text trimming
* no automatic entity decoding
* no automatic attribute parsing

...and that is ok ❤.


## Credits

We build on the awesome work done by [easysax](https://github.com/vflash/easysax).

`/saxen/` is named after [Sachsen](https://en.wikipedia.org/wiki/Saxony), a federal state of Germany. So geht sächsisch!

## LICENSE

MIT

---
_Source: https://npm.io/package/saxen · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
