# N3.js streams RDF triples as the parser finds them

> N3.js is an implementation of the RDF/JS low-level specification that parses and writes Turtle, TriG, N-Triples, N-Quads and Notation3, asynchronously and in a streaming fashion so a document larger than memory can be processed, with a DataFactory that assigns XSD datatypes automatically. It suits applications that ingest or emit linked data without loading it all at once.

**rdfjs/N3.js** — Lightning fast, spec-compatible, streaming RDF for JavaScript

- Repository: https://github.com/rdfjs/N3.js
- Website: http://rdf.js.org/N3.js/
- Stars: 797 · Forks: 146
- Language: JavaScript
- License: NOASSERTION
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/rdfjs-n3-js

## A streaming parser rather than a document loader

The library describes itself as lightning fast, asynchronous, streaming RDF for JavaScript, and the three words in that title are the design. Parsing and writing are asynchronous, so triples arrive as soon as possible. They are streaming, so streams are parsed as data comes in, which the README says means you can parse files larger than memory. And they are fast. The practical consequence is that the callback for each quad fires during the parse rather than after it, so a consumer can start writing results to a database while the source is still being read. The formats are named in the same list. Parsing covers Turtle, TriG, N-Triples, N-Quads and Notation3, writing covers the first four, and there is in-memory storage of triples and quads for the cases where a caller wants a graph rather than a stream.

## Two ways to receive the parse callback

The parser transforms a document into quads through a callback, and the first form is a single function whose first argument is an optional error, whose second is a quad, and whose third is the prefix map:

```JavaScript
const parser = new N3.Parser();

parser.parse(tomAndJerry,
  (error, quad, prefixes) => {
    if (quad)
      console.log(quad);
    else
      console.log("# That's all, folks!", prefixes);
  });
```

When there are no more quads, the callback is invoked one last time with `null` for the quad and the prefix hash as the third argument. The alternative is an object with named callbacks for quads, prefixes, comments and token processing, which is what you want when you care about more than the quads themselves:

```JavaScript
const parser = new N3.Parser();

parser.parse(tomAndJerry, {
  onQuad: (err, quad) => { console.log(quad); },
  onPrefix: (prefix, iri) => { console.log(prefix, 'expands to', iri.value); },
  onComment: (comment) => { console.log('#', comment); },
});
```

The named form is also where the token hooks live, with `onToken` running immediately before a lexer token is processed and `onTokenEnd` running immediately afterwards, including when processing throws.

## The DataFactory assigns XSD datatypes for you

Terms are created through `N3.DataFactory`, and the factory functions are where the type semantics are decided. When no language or datatype is supplied, `literal` automatically assigns XSD datatypes to JavaScript booleans, numbers, BigInts and valid `Date` objects. Integer-valued numbers below 1e21 and all BigInts become `xsd:integer`, so a BigInt is the way to keep an integer exact beyond `Number.MAX_SAFE_INTEGER`, and other numbers become `xsd:double`. Dates are converted to UTC with `Date.prototype.toISOString()` and receive the `xsd:dateTime` datatype:

```JavaScript
const created = literal(new Date('2017-04-27T14:39:48.901Z'));
console.log(created.value);          // 2017-04-27T14:39:48.901Z
console.log(created.datatype.value); // http://www.w3.org/2001/XMLSchema#dateTime
```

There is a rule about how to create terms, and the reason is validation. The README says to always create terms through a data factory such as `N3.DataFactory` and not by instantiating the term classes directly, because direct construction is deprecated and the factory functions are where term validation can be applied. It also states the assumption the library makes about terms from elsewhere, in line with the RDF/JS specification, that the value of any term it receives, whether from its own factory or from another implementation, was already validated when the term was created, and that N3.js does not re-validate terms.

## Custom directives and a separate stream parser

Formats that extend a standard with their own directives can register those names through the `directives` option. A registered directive is accepted between statements, outside graph blocks and formulas, as `@name .` in Turtle, TriG and N3, and as a case-insensitive `NAME` in all formats including N-Triples and N-Quads. The `onDirective(name)` callback receives each one in order with the quads, and `N3.StreamParser` emits them as `directive` events. This is the extension point that matters for anyone working with a dialect that adds statements of its own, since without registration such a statement is a parse error. The names consist of letters only, according to the README, and the repository carries a `perf/` directory alongside `src/`, `spec/` and `test/`, which is where the performance work for the parser lives.

## Using it in a browser without a build step

The library is published to npm as `n3` and is used from Node with a require:

```bash
$ npm install n3
```

```JavaScript
const N3 = require('n3');
```

In a browser it works through webpack or browserify, and the README notes you will need to create a UMD bundle and supply a name, for example with the `-s N3` option in browserify. There is also a CDN route that avoids any tooling. As a classic script it exposes a global `N3`:

```html
<script src="https://unpkg.com/n3/browser/n3.min.js"></script>
```

or as an ES module you can import directly:

```html
<script type="module">
  import { Store, Parser, Writer } from 'https://unpkg.com/n3/browser/n3.esm.min.js';
</script>
```

That the browser build is a first class deliverable rather than an afterthought is visible in the repository, where the `browser` directory is listed among the published files next to `src` and `lib`, and the build scripts have separate node and browser targets.

## Spec compliance is checked by other people's suites

The project takes specification compatibility seriously enough to run other implementations' test suites. The development dependencies include `rdf-test-suite`, which is the W3C test suite collection, and `rdf-isomorphic`, a canonicalisation library used to compare graphs for equality, along with `deep-taxonomy-benchmark` for the performance work. The package has a `spec` script that runs `node spec/run.js`, separate from the `test` script that runs Jest, so there is a distinction between the unit tests and the specification conformance run. The manifest declares support for Node 12 and later, and the published files include `.babelrc`, `src`, `lib` and `browser`, with Babel 8 doing the node build and a separate script producing the browser bundles in both IIFE and ESM forms. The licence in the manifest is MIT, while the platform classifies the repository licence as a custom one, so the `LICENSE.md` file is the text to read.

## A 3.0 alpha line alongside the 2.x line

The release history shows two tracks in flight. The 2.x line is still being patched, with v2.13.12 published on 2026-10-10, while the 3.0 line is in alpha, with v3.0.0-alpha.12 on 2026-10-08 and v3.0.0-alpha.9 on 2026-06-18's successor on 2026-10-06. The repository is not archived and the last push was on 2026-10-19, so both lines are being worked on. The version in the package manifest is `2.0.1`, which is the published package version rather than the tag numbering, and that discrepancy is worth checking when you pin a version, since the npm version and the git tag do not use the same number. The README's own heading for the project is the older phrase, lightning fast, asynchronous, streaming RDF for JavaScript, and the repository description adds spec-compatible to it, which is the claim the RDF/JS and W3C suite dependencies are there to back.

## Conclusion

Adopt N3.js when your JavaScript application has to read or write linked data larger than memory, or needs to process a feed as it arrives, since the parser and writer are asynchronous and streaming rather than whole document operations. Do not adopt it if you want a plain object graph of JavaScript objects with no RDF semantics, because the terms, datatypes and graphs are the point rather than an implementation detail. Verify first which formats you need, since parsing covers Turtle, TriG, N-Triples, N-Quads and Notation3 while writing omits Notation3, and check the version line you take, because a 3.0 alpha series is being published alongside the 2.x line and the two may not behave identically.

## FAQ

### How do I install and import N3.js?

Install the npm package with `npm install n3` and require it with `const N3 = require('n3');`. In a browser you can either bundle it with webpack or browserify, or load it from a CDN as a classic script exposing a global `N3`, or as an ES module from the unpkg browser build.

### Which RDF formats can N3.js parse and write?

Parsing covers Turtle, TriG, N-Triples, N-Quads and Notation3, while writing covers Turtle, TriG, N-Triples and N-Quads. There is also in-memory storage of triples and quads.

### How does N3.js decide the datatype of a literal?

When no language or datatype is supplied, the `literal` factory assigns XSD datatypes automatically: integers below 1e21 and all BigInts become `xsd:integer`, other numbers become `xsd:double`, booleans get their own type, and Date objects are converted to UTC and given `xsd:dateTime`.

### Can N3.js handle a file larger than memory?

Yes, because parsing is streaming. The README says streams are parsed as data comes in, so you can parse files larger than memory, and the parse callback receives quads as soon as they are available rather than after the whole document is read.

## Sources

- [Issues](https://github.com/rdfjs/N3.js/issues)
- [Project website](http://rdf.js.org/N3.js/)
- [rdfjs/N3.js on GitHub](https://github.com/rdfjs/N3.js)
- [README](https://github.com/rdfjs/N3.js/blob/main/README.md)
- [Releases](https://github.com/rdfjs/N3.js/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/rdfjs-n3-js
