← Chrome 154 reference

v154 · web api · networking

Add options bag to WebSocket constructor

Limited availability

  • Chrome · implemented, behind an experimental flag (targeted at 154)
  • Edge · not separately reported
  • Firefox · no signal
  • Safari · positive standards position, not shipped

Not a Baseline feature: the option bag has no web-features entry on webstatus.dev (queried 2026-09-20; only the host websockets feature exists, Baseline widely available — that covers the classic constructor, not this overload). BCD has no subfeature key for it either (the constructor entry records only the host constructor and the relative-URL update, verified 2026-09-20).

An overload of the WebSocket() constructor: the second argument may now be a WebSocketInit options dictionary (new WebSocket(url, { protocols: ["soap"] })) instead of a bare string or array of subprotocol names. The dictionary initially carries a single member, protocols, and exists chiefly as an extension point — mirroring the Fetch API's option-bag style — so future connection options don't need new positional arguments.

Experimental — not yet enabled by default

The milestone=154 listing files this feature under “Enabled by default”, but as of 2026-09-20 that is a target, not a shipped state: the runtime flag record for WebSocketOptionBag is still status: "experimental" at trunk, the CL that promotes it to stable (CL 8256918) is open and unmerged, the ChromeStatus record's own implementation status reads “Proposed”, and the defining spec change (whatwg/websockets PR #76) is open with active web-compat review. To test locally:

chrome --enable-blink-features=WebSocketOptionBag

or enable chrome://flags/#enable-experimental-web-platform-features. Feature-detect before relying on it — with the flag off, an options object is coerced to the string "[object Object]" and the constructor throws a SyntaxError:

function supportsWebSocketOptions() {
  try {
    // An empty bag is valid where the overload exists, and an invalid
    // protocol string where it does not.
    new WebSocket("wss://example.invalid/", {}).close();
    return true;
  } catch {
    return false;
  }
}

at a glance

What it isA third accepted type for the WebSocket() constructor's second argument: alongside DOMString and sequence<DOMString>, a WebSocketInit dictionary whose protocols member lists subprotocols (spec PR diff)
WhyExtensibility: an option bag mirrors fetch()'s RequestInit, so future per-connection options need no new positional parameters. Chromium's concrete motivation is a targetAddressSpace option for Local Network Access (issue #42)
Behavior changePassing a plain non-iterable object as the second argument today coerces to the string "[object Object]" and rejects with SyntaxError; with the overload it parses as an options dictionary. This is the web-compat question under measurement on the spec PR
Milestone listingChrome 154 — listed under “Enabled by default” in the milestone=154 listing (fetched 2026-09-20; the listing is authoritative for filing). See the warn-block for the actual flag state
Runtime featureWebSocketOptionBag (experimental) in runtime_enabled_features.json5; implemented by CL 8197225 (merged 2026-08-12); stable promotion pending in CL 8256918
WPTwebsockets/constructor/option-bag.any.js (upstreamed with the implementation CL): an empty bag is accepted; a protocols array negotiates the first supported subprotocol
ChromeStatus5080055102439424 — Add options bag to WebSocket constructor (blink component Blink>Network>WebSockets; tracking bug crbug.com/542670554)
Source: whatwg/websockets PR #76 diff; websocket_init.idl; ChromeStatus API record

why it exists

whatwg/websockets issue #42 (open since 2015) collects long-standing requests to make the WebSocket constructor extensible the way fetch() is: headers, cache mode, and other per-connection knobs have nowhere to go when the only extra parameter is a protocols list. The option bag itself doesn't add those capabilities — it adds the place for them. Chromium's immediate use is targetAddressSpace, needed so pages can opt WebSocket connections into Local Network Access checks the way fetch(url, { targetAddressSpace }) already can; that member is tracked as its own Chrome 154 feature (Support targetAddressSpace option for WebSockets) and rides on this extension point.

Source: issue #42; blink-dev Intent to Prototype (2026-08-05); chromestatus 4779920606756864

how the second argument is routed

The constructor's second parameter becomes the three-way union (DOMString or sequence<DOMString> or WebSocketInit). Which branch a value takes follows the Web IDL union conversion algorithm, so the routing is fully deterministic:

You passBranch takenResulting protocols
"soap" (string)DOMString — unchanged classic form["soap"]
["soap", "wamp"] (array, or any iterable object)sequence<DOMString> — unchanged classic form["soap", "wamp"]
{ protocols: ["soap"] } (non-iterable object)WebSocketInit dictionary — new["soap"]
{} (empty object)WebSocketInit with defaults[] (none)
undefined / omittedparameter default [][] (none)
{ protocols: "soap" } (bare string member)WebSocketInit, then TypeError— see the member's type

The spec PR's constructor step is a two-liner (quoted from the diff): “If protocolsOrOptions is a WebSocketInit dictionary, set protocols to protocolsOrOptions["protocols"]. Otherwise, set protocols to protocolsOrOptions.” Everything downstream — protocol validation, the opening handshake — is the existing algorithm.

In Chromium, dom_websocket.cc branches on RuntimeEnabledFeatures::WebSocketOptionBagEnabled(): with the flag on, the argument converts through the generated union V8UnionStringOrStringSequenceOrWebSocketInit and ParseConstructorOptionsWithBag() reads the dictionary; with the flag off, the pre-existing two-way union applies and an object coerces to a (rejected) protocol string. The declared IDL parameter is temporarily any so the two behaviors can coexist (websocket.idl, TODO note referencing crbug.com/542670554).

Source: Web IDL § ES union conversion; PR #76 diff; dom_websocket.cc; websocket.idl

syntax

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

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

[Exposed=(Window,Worker)]
interface WebSocket : EventTarget {
  constructor(USVString url,
              optional (DOMString or sequence<DOMString> or WebSocketInit) protocolsOrOptions = []);
  // … rest of the interface unchanged
};

Shipping Chromium dictionary IDL (websocket_init.idl at trunk, 2026-09-20 — quoted verbatim, including the flag-gated Chromium extension):

dictionary WebSocketInit {
    sequence<DOMString> protocols = [];
    [RuntimeEnabled=LocalNetworkAccessWebSocketsTargetAddressSpace] IPAddressSpace targetAddressSpace;
};

The WebSocket living standard does not yet contain WebSocketInit (0 occurrences, verified 2026-09-20) — PR #76 must merge first. Full member documentation: the constructor overload and the WebSocketInit dictionary.

Source: PR #76 diff; websocket_init.idl; websockets.spec.whatwg.org

reference

examples

Both spellings of the same connection — the classic positional form and the option bag:

// Classic (unchanged, works everywhere):
const a = new WebSocket("wss://example.com/chat", "soap");
const b = new WebSocket("wss://example.com/chat", ["soap", "wamp"]);

// Option bag (Chrome 154 behind the WebSocketOptionBag flag):
const c = new WebSocket("wss://example.com/chat", { protocols: ["soap"] });

// NOTE: the member is a sequence — a bare string throws a TypeError:
// new WebSocket("wss://example.com/chat", { protocols: "soap" });  // TypeError

Negotiation is unchanged: the server picks one offered subprotocol, and the chosen one appears on socket.protocol after open, exactly as with the positional forms.

Live example from the Chrome Platform Showcase (route HEAD-checked 200, 2026-09-20). Sibling concept demos: subprotocol negotiation and why a dictionary. The demo needs the WebSocketOptionBag flag to exercise the new form.Source: chrome-platform-showcase
Source: forms per the PR #76 IDL and the WPT option-bag tests; the TypeError note is gendn-derived from Web IDL sequence conversion (labelled accordingly)

browser compatibility

Interim table. BCD has no key for the option-bag overload (the WebSocket() constructor entry carries only the host constructor and the relative-URL subfeature, verified 2026-09-20), and webstatus.dev has no web-feature for it. Rows compiled from the linked primary sources.

BrowserSupportEvidence
Chrome / Edge (Chromium)Implemented; behind WebSocketOptionBag (experimental) — stable promotion targeted at 154, CL open as of 2026-09-20CL 8197225 (merged 2026-08-12); flag record; CL 8256918
FirefoxNo signalChromeStatus API record vendor views; position requested at mozilla/standards-positions #1444
SafariPositive standards position (“Support”); not shippedWebKit/standards-positions #708; ChromeStatus API record

For orientation, the host WebSocket() constructor itself is Baseline widely available (Chrome 5+, Edge 12+, Firefox 11+, Safari 5+ per BCD) — only the dictionary overload is new.

Source: as linked per row; webstatus.dev websockets feature

security and privacy

Source: PR #76 review thread; Local Network Access spec; websocket_init.idl

specifications

DocumentStatus
whatwg/websockets PR #76 — Add WebSocketInit option to constructorOpen, unmerged (last updated 2026-09-14, checked 2026-09-20) — the normative text this page quotes; review is measuring the object-coercion web-compat risk
whatwg/websockets issue #42 — Extensibility of the WebSocket constructorThe originating proposal (2015–) and de-facto explainer; records implementer and user demand
WebSockets living standardHost specification; does not yet contain WebSocketInit (0 occurrences, verified 2026-09-20)
blink-dev — Intent to Prototype: WebSocket constructor option bagPosted 2026-08-05; tracking bug 542670554. No Intent-to-Ship thread is linked from the ChromeStatus record as of 2026-09-20
Repository and thread records are linked above; contents fetched 2026-09-20.

see also