← Add options bag to WebSocket constructor

v154 · experimental · dictionary

WebSocketInit

The options dictionary the WebSocket() constructor accepts as its second argument. The proposed standard shape has a single member, protocols; Chromium's IDL additionally declares a flag-gated targetAddressSpace extension. The dictionary's real payload is extensibility: future connection options get named members here instead of new positional parameters.

Experimental

Behind --enable-blink-features=WebSocketOptionBag (or chrome://flags/#enable-experimental-web-platform-features). The defining spec change, whatwg/websockets PR #76, is open as of 2026-09-20, so the dictionary's name and members can still change in review. See the overview warn-block for feature detection.

syntax

Proposed spec IDL (whatwg/websockets PR #76, quoted verbatim from the diff, 2026-09-20):

dictionary WebSocketInit {
  sequence<DOMString> protocols = [];
};

Shipping Chromium IDL (websocket_init.idl at trunk, 2026-09-20 — quoted verbatim):

dictionary WebSocketInit {
    sequence<DOMString> protocols = [];
    [RuntimeEnabled=LocalNetworkAccessWebSocketsTargetAddressSpace] IPAddressSpace targetAddressSpace;
};
Source: PR #76 diff; websocket_init.idl

the protocols member

Typesequence<DOMString> — strictly a sequence. A bare string is a TypeError: Web IDL sequence conversion requires an iterable object, so { protocols: "soap" } throws while the positional second argument "soap" still works (the union's DOMString branch handles that case, not this member). Write { protocols: ["soap"] }. Note the ChromeStatus summary's { protocols: "soap" } example does not match the IDL as written in either the spec PR or Chromium
Default[] — offer no subprotocol, exactly like omitting the classic second argument
MeaningThe list of subprotocol names offered in the opening handshake's Sec-WebSocket-Protocol header; validation and negotiation are the classic algorithm of the living standard (invalid or duplicate names still reject with SyntaxError)
EquivalencePer the PR's constructor step, the member's value simply becomes the protocols list — { protocols: x } and positional x (array form) are indistinguishable downstream
Source: PR #76 diff; Web IDL § sequence conversion; living standard; ChromeStatus record

the targetAddressSpace member (Chromium extension)

Chromium's dictionary declares a second member, targetAddressSpace (an IPAddressSpace: "public", "private", or "local"), gated behind its own LocalNetworkAccessWebSocketsTargetAddressSpace runtime flag in the flag registry. It mirrors fetch()'s option of the same name from the Local Network Access spec and is tracked as its own Chrome 154 feature, Support targetAddressSpace option for WebSockets — it is documented there, not here; this page records only that the extension point is already in use. It is not part of PR #76.

Source: websocket_init.idl; Local Network Access; chromestatus 4779920606756864

inputs

The dictionary is pure input: script constructs it and passes it to the constructor; nothing ever hands a WebSocketInit back to script. Unknown properties on the object are ignored per standard Web IDL dictionary conversion (only declared members are read), so a typo like { protocol: "soap" } silently offers no subprotocol rather than erroring — check socket.protocol after open if negotiation matters.

Source: Web IDL § dictionary conversion; PR #76 diff

outputs

No direct output. The dictionary's observable effect is the offered-subprotocol set in the handshake and, after negotiation, the value of socket.protocol (the server's chosen subprotocol, or the empty string). The WPT tests assert exactly this: an empty bag connects successfully with no subprotocol, and { protocols: ['foobar', 'foobar2'] } yields ws.protocol === 'foobar' against an echo server that accepts the first offer.

Source: WPT option-bag.any.js; living standard

errors

TypeErrorprotocols set to a non-iterable value (a bare string, a number, null): sequence conversion fails during argument processing, before any network activity
SyntaxError DOMExceptionSequence conversion succeeded but a name is not a valid subprotocol token, or the list contains duplicates — the classic validation of the living standard, identical for positional and dictionary spellings
Silently ignoredUnknown members (including typos) — standard dictionary conversion reads only declared members
Source: Web IDL § sequence; § dictionary; living standard

context and exposure

Available wherever the constructor is: Window and Worker contexts, when Chromium's WebSocketOptionBag runtime feature is enabled (flag record: status: "experimental", 2026-09-20). As a dictionary it has no prototype or global-object presence — there is nothing to feature-detect on window; detection goes through constructor behavior (see the overview).

Source: websocket_init.idl; runtime_enabled_features.json5

lifecycle

The dictionary is read once, synchronously, during the constructor call (ParseOptionBag() in dom_websocket.cc copies protocols into the connection's protocol vector). Mutating the object afterwards has no effect on the connection; the object holds no live relationship to the socket. Future members (like targetAddressSpace) follow the same read-once model.

Source: dom_websocket.cc

examples

// Correct: protocols is a sequence.
const ws = new WebSocket("wss://example.com/chat", {
  protocols: ["chat.v2", "chat.v1"],
});

ws.addEventListener("open", () => {
  if (ws.protocol === "") {
    // Server accepted the connection without choosing a subprotocol.
  }
});

// TypeError — a bare string is not a sequence:
// new WebSocket("wss://example.com/chat", { protocols: "chat.v2" });

// Silent no-op — unknown members are ignored (typo offers NO subprotocol):
// new WebSocket("wss://example.com/chat", { protocol: "chat.v2" });
Source: gendn-derived from the PR #76 IDL and Web IDL dictionary semantics (labelled accordingly); array form matches the WPT tests

browser compatibility

Interim table. No BCD key for the dictionary (verified 2026-09-20); rows compiled from primary sources.

BrowserSupportEvidence
Chrome / Edge (Chromium)Implemented behind WebSocketOptionBag (experimental); stable promotion targeted at 154, pending as of 2026-09-20CL 8197225; flag record; CL 8256918
FirefoxNo signalChromeStatus API record; mozilla/standards-positions #1444
SafariPositive position, not shippedWebKit/standards-positions #708
Every row above links its own source record.

security and privacy

The dictionary grants no capability of its own: protocols feeds the same validated handshake header as the classic argument. Two considerations are real: the web-compat shift (objects stop throwing and start meaning “empty options” — under measurement on the spec PR), and the fact that future members carry their own security surface — the first, targetAddressSpace, exists precisely to integrate WebSockets with Local Network Access protections and is security-reviewed as its own feature.

Source: PR #76 review thread; Local Network Access

see also