
# 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](/nstdlib/host).

## Importing a builtin

Each `node:` builtin is its own subpath, matching the name you'd use with
Node itself:

```js
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](/nstdlib/install) for
what that means and the [API index](/api) 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`:

```js
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 answer
```

Most 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](/api) 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:

```js
// 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](/api))
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 real `globalThis.crypto`.
- **DNS** (`dns.lookup`, `dns.resolve*`) works over DNS-over-HTTPS, because
  it only needs `fetch`, 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](/guide) does, and it's what the [Host](/nstdlib/host)
  page covers.

- **`require()` needs a runtime that allows compiling code from a string.**
  It's built on `new Function(source)`, so a page under a
  Content-Security-Policy without `unsafe-eval`, or a worker running with
  code generation from strings disabled, gets no `require()` 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 `process` or `Buffer` global 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, see `nstdlib/globals` in [Host](/nstdlib/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](/api).
