Library / SDK
h3js/crossws avatar
h3js/crossws

crossws gives one WebSocket server API to four runtimes

🔌 Cross-platform WebSocket Servers for Node.js, Deno, Bun and Cloudflare Workers.

738 stars44 forksTypeScriptNOASSERTION

At a glance

What is it?
The crossws toolkit is a typed, tree-shakable abstraction over WebSocket servers that runs on Node.js, Deno, Bun and Cloudflare Workers, and its design choice is to ship adapters and server implementations as separate conditional exports so a bundle carries only the runtime it needs.
Who is it for?
crossws suits a team whose server code has to run on more than one JavaScript runtime, since the same hooks API and the same types work on Node.js, Deno, Bun and Cloudflare Workers. It is the wrong choice if you only deploy to Node.js, where ws is already installed with the runtime and the abstraction is overhead.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One hooks API across four runtimes

crossws is described as an elegant, typed and simple toolkit to implement cross-platform WebSocket servers, and the feature list is short and specific about what it does. It integrates with Bun, Cloudflare Workers, Deno and Node.js and with any compatible web framework, and it is prebundled with ws for Node.js support, with an alternative and much faster uWebSockets adapter also available. Three of the claims are about developer experience rather than capability: it is extremely lightweight and tree-shakable with conditional ESM exports, it has a high-performance and simple hooks API without per-connection callback creation, and it has a typed hooks API with developer-friendly object inspection. The absence of per-connection callback creation is the one that matters for anyone who has profiled a WebSocket server, since allocating a closure per connection is exactly the kind of cost that shows up under load. The audience is a team with a multi-runtime deployment target, and the documentation site is crossws.h3.dev.

The exports map is the architecture

The package.json is where the design becomes clear, because the exports map is long and conditional. There is a root export, a sync entry, and then a family of adapter subpaths covering bun, bunny, deno, cloudflare, sse, node, vercel and uws. Then there is a parallel family of server subpaths, with server/bun, server/bunny, server/deno and server/node, and a server entry that itself resolves by condition, with deno, bun, workerd, node and a default. The websocket export is the most intricate, resolving to different files for browser, worker, bun, deno, edge-light, workerd, node and a default, and there is a separate websocket/sse entry point. The package is marked type module and sideEffects false, which is what makes the tree-shaking claim hold, and the main, module and types fields point at the built mjs output. For a consumer this means the runtime is chosen by the bundler's condition resolution rather than by runtime detection, which is fast and predictable but means the import path has to match the deployment target.

Node.js gets two implementations, and the faster one is optional

The Node.js story is the most detailed part of the feature list. crossws supports Node.js via the prebundled ws package, or via uWebSockets.js, and the README describes the uWebSockets route as an alternative and much faster adapter with its own documentation page. That is an unusual thing to offer in a cross-platform toolkit, because the two implementations have quite different performance characteristics and different operational footprints, and a team that deploys to Node.js on a small number of machines may find the extra choice unnecessary. The Bunny adapter appears in the list too, alongside the Vercel and Cloudflare ones, so the set of targets is wider than the four named in the description. The tests and the benchmarks live in the repository as test/ and bench/ directories, and the build is driven by build.config.ts, so the toolchain is a typed build rather than a hand-written bundler configuration.

Local development is a pnpm workspace with play scripts

The contribution section is short and gives the local workflow. You clone the repository, install the latest LTS version of Node.js, enable Corepack with corepack enable, install dependencies with pnpm install, and run examples using the pnpm play: scripts. The repository layout is consistent with a modern TypeScript package. There is src/ for the source, test/ with a vitest.config.mjs, types/ for the type declarations, a bench/ directory, a playground/ directory, a docs/ directory, an examples/ directory containing a socket.io example, a pnpm-workspace.yaml, and renovate.json for dependency updates. Linting and formatting run through oxlint and oxfmt, given the .oxlintrc.json and .oxfmtrc.json files, and the package ships under the MIT licence with the readme noting it is made by pi0 and the community.

Where the abstraction is the wrong bet

Three limits are worth naming. The first is that the payoff depends on the multi-runtime premise. If you deploy to Node.js only, ws comes with the runtime, so the abstraction is a layer you maintain rather than one you save, and a team that only ever runs one runtime will be better served by depending on the WebSocket implementation directly. The second is that the adapter resolution is silent when it is wrong. Because the map falls back to a default condition, an import that does not match your deployment target still resolves, and you find out at runtime rather than at build time. The third is the boundary of the design. The package ships an SSE adapter and a websocket/sse export, but the feature list talks about WebSocket servers, so if your requirement is server sent events with reconnection semantics and a proper event stream, an SSE adapter is not the same thing as an SSE server. The fourth is that this is a 0.x package with a fast moving export map, and the versions in the readme, 0.4.13 as a release and 0.4.15 in the manifest, suggest the readme and the package move independently.

Editorial conclusion

crossws suits a team whose server code has to run on more than one JavaScript runtime, since the same hooks API and the same types work on Node.js, Deno, Bun and Cloudflare Workers. It is the wrong choice if you only deploy to Node.js, where ws is already installed with the runtime and the abstraction is overhead. It is also the wrong choice if you need server sent events, because the package offers an SSE adapter rather than a full SSE server, and the WebSocket export map shows that the SSE path is a separate entry point rather than a mode of the same server. Before adopting it, check the exports map against the runtime you deploy, since the resolution is per condition and a wrong import silently falls back to a default. The last push was on 2026-10-18 and the current release is v0.4.13 from 2026-10-06.

Frequently asked questions

Which runtimes does crossws support?

The README says it integrates with Bun, Cloudflare Workers, Deno and Node.js and with any compatible web framework, and the exports map also carries adapters for bunny, vercel, sse and uws. Node.js is supported through the prebundled ws package or through the alternative and much faster uWebSockets adapter.

How does the crossws hooks API work?

The feature list describes a high-performance and simple hooks API without per-connection callback creation, with typed hooks and developer-friendly object inspection. The API is documented at crossws.h3.dev rather than in the repository readme.

Is crossws tree-shakable?

The README lists extremely lightweight and tree-shakable conditional ESM exports as a feature, and the package.json backs that up with sideEffects set to false and an exports map that resolves adapters and server implementations per condition, with separate entries for browser, worker, bun, deno, edge-light, workerd, node and a default.

Official sources

  1. h3js/crossws on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/h3js-crossws.svg)](https://hysenlabs.com/projects/h3js-crossws)