websockets/ws · error · Error

ws does not work in the browser. Browser clients must use th

Error message

ws does not work in the browser. Browser clients must use the native WebSocket object

What it means

This error is thrown synchronously whenever the 'ws' module is imported in a browser bundle. The package.json declares a 'browser' field (and an exports.browser condition) that maps to browser.js, whose sole export is a function that throws this Error. Bundlers like webpack and vite resolve this stub automatically when the build target is a browser, replacing the full Node.js implementation. The library does this deliberately because ws depends on Node core modules (net, http, crypto) that cannot run in a browser environment.

Source

Thrown at browser.js:4

'use strict';

module.exports = function () {
  throw new Error(
    'ws does not work in the browser. Browser clients must use the native ' +
      'WebSocket object'
  );
};

View on GitHub (pinned to ae1de54330)

Solutions

  1. Replace `new WebSocket(...)` from ws with the browser-native `new WebSocket(url)` in any code that runs in the browser.
  2. If the module must be isomorphic, guard the import behind an environment check (typeof window !== 'undefined') and only require ws on the server side.
  3. Configure your bundler to alias 'ws' to a stub or to the native WebSocket only for browser builds, and keep the real ws for server builds.
  4. If you genuinely need ws in a browser-like environment (e.g. jsdom/electron renderer), ensure the bundler does not resolve the 'browser' condition — force Node resolution for that entry point.

Example fix

// before (shared module, breaks in browser)
const WebSocket = require('ws');
const ws = new WebSocket('ws://example.com');

// after (isomorphic — use native in browser, ws on server)
const WebSocket =
  typeof window !== 'undefined'
    ? window.WebSocket
    : require('ws');
const ws = new WebSocket('ws://example.com');
Defensive patterns

Strategy: validation

Validate before calling

// Detect a browser environment before importing ws
const isBrowser =
  typeof window !== 'undefined' && typeof window.WebSocket !== 'undefined';

if (isBrowser) {
  throw new Error(
    'Use the native browser WebSocket (window.WebSocket) instead of the ws package.'
  );
}

Type guard

// Type guard for the browser-native WebSocket constructor
function isBrowserWebSocket(Ctor) {
  return typeof Ctor === 'function' && Ctor.prototype && 'readyState' in Ctor.prototype;
}

Try / catch

// Rarely useful: catching the throw from requiring ws in a browser bundle
let WebSocketImpl;
try {
  WebSocketImpl = require('ws');
} catch (err) {
  if (typeof window !== 'undefined') {
    WebSocketImpl = window.WebSocket;
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: A bundler (webpack/vite/rollup/esbuild) with a browser target resolves the 'browser' field in ws's package.json and substitutes browser.js for index.js. The throw fires the moment any code does `require('ws')` or `import ... from 'ws'` in the browser build. It also fires if a developer explicitly imports 'ws/browser.js'.

Common situations: A developer copies Node.js WebSocket client code into a frontend React/Vue/Angular project without switching to the browser-native WebSocket. A shared/isomorphic module unconditionally requires 'ws' and is bundled for the browser. A bundler misconfiguration where the 'browser' field resolves but no fallback shim is provided. Migrating a server-rendered app to client-side rendering.

Related errors


AI-assisted analysis of websockets/ws@ae1de54330 (2026-08-03). Data as JSON: /data/errors/ec529b1f7329accb.json. Report an issue: GitHub.