Usage
Import Node.js builtins from nstdlib and use them across different JavaScript environments.
This page covers day-to-day use: importing a builtin, using it with a bundler, and what changes when your code runs in a browser or a worker. To connect a filesystem, DNS resolver, or WebCrypto implementation supplied by your runtime, see Host.
#Importing a builtin
Each node: builtin is its own subpath, matching the name you'd use with
Node itself:
import fs from "nstdlib/fs";
import path from "nstdlib/path";
import { EventEmitter } from "nstdlib/events";
const emitter = new EventEmitter();
emitter.on("greet", (name) => console.log(`hello, ${name}`));
emitter.emit("greet", "world");
console.log(path.join("a", "b", "c")); // 'a/b/c'Most modules offer both a default export and named exports, the same way
Node's own builtins do - import fs from "nstdlib/fs" and
import { readFileSync } from "nstdlib/fs" both work and refer to the same
function. Use whichever your code already expects; there's no subpath that
only supports one form.
A module that isn't published as an entry can't be imported at all, no matter how you write the specifier - see Installation for what that means and the API index for the full list of entries.
Two modules worth calling out because they genuinely work end to end, not
just import cleanly - crypto's randomness half runs on the host's real
WebCrypto, and dns resolves over DNS-over-HTTPS on the host's fetch:
import crypto from "nstdlib/crypto";
import dnsPromises from "nstdlib/dns/promises";
crypto.randomUUID(); // real entropy, Node's own RFC 4122 v4 serialiser
await dnsPromises.resolveMx("gmail.com"); // a real DNS answerMost other modules that need direct access to the operating system don't have that backing yet. Check a module's own page in the API index before relying on it.
#Using it with a bundler
nstdlib/* entries are plain ES modules with no bundler-specific tricks, so
any modern bundler (Vite, Rolldown, webpack, esbuild, Rollup) can import,
code-split and tree-shake them like any other package.
Each entry's subpath is the builtin's own name, which means redirecting
node:* specifiers at nstdlib is a plain alias in your bundler's own
config - nstdlib doesn't do this for you, but nothing stops you from
setting it up yourself:
// vite.config.js
export default {
resolve: {
alias: {
"node:fs": "nstdlib/fs",
"node:path": "nstdlib/path",
"node:events": "nstdlib/events",
},
},
};Add only the specifiers your code actually uses. An alias for a module that doesn't work the way your code needs (see the API index) will still resolve and import - it just won't behave like Node's version at the call you rely on.
#In a browser or a worker
The pure-JavaScript parts of the standard library - path, events,
buffer, util, url, assert, stream and more - run in a browser tab or
a worker the same way they run under Node, because they never needed an
operating system to begin with.
Parts that do need one behave differently depending on what the host provides:
Randomness and WebCrypto (
crypto.randomBytes,randomUUID,crypto.subtle) work natively, because every modern JavaScript engine ships a realglobalThis.crypto.DNS (
dns.lookup,dns.resolve*) works over DNS-over-HTTPS, because it only needsfetch, which browsers and most workers have natively.Everything that needs a real filesystem, a socket, or OpenSSL has no substitute in a plain browser or worker. Calling it doesn't hang or return
undefined- it throws, naming exactly what's missing:Error: [nstdlib] internalBinding('fs').open() is not implemented: this runtime has no such binding, and no host implementation is installed on `globalThis.node_binding_fs_open`.A host can fill that gap by installing the named global itself - that's what nstd does, and it's what the Host page covers.
require()needs a runtime that allows compiling code from a string. It's built onnew Function(source), so a page under a Content-Security-Policy withoutunsafe-eval, or a worker running with code generation from strings disabled, gets norequire()from this library at all - not a degraded one, none.import()of a CommonJS module needs the same capability, because it uses the same compiler.import()of a registered builtin or a JSON file does not.There's no
processorBufferglobal by default. Node installs both from C++ before your code runs; a browser or worker has neither. If your code (or a dependency) expects them, seenstdlib/globalsin Host.
For the full, per-module picture of what's verified, what's API-only, and what doesn't work at all, see the API index.