v154 · web api · networking
Add options bag to WebSocket constructor
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.
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 is | A 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) |
|---|---|
| Why | Extensibility: 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 change | Passing 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 listing | Chrome 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 feature | WebSocketOptionBag (experimental) in runtime_enabled_features.json5; implemented by CL 8197225 (merged 2026-08-12); stable promotion pending in CL 8256918 |
| WPT | websockets/constructor/option-bag.any.js (upstreamed with the implementation CL): an empty bag is accepted; a protocols array negotiates the first supported subprotocol |
| ChromeStatus | 5080055102439424 — Add options bag to WebSocket constructor (blink component Blink>Network>WebSockets; tracking bug crbug.com/542670554) |
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.
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 pass | Branch taken | Resulting 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 / omitted | parameter 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).
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.
reference
WebSocket(url, protocolsOrOptions)— the constructor overload: parameters, routing, errors, lifecycleWebSocketInit— the options dictionary and itsprotocolsmember
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.
WebSocketOptionBag flag to exercise the new form.Source: chrome-platform-showcasebrowser 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.
| Browser | Support | Evidence |
|---|---|---|
| Chrome / Edge (Chromium) | Implemented; behind WebSocketOptionBag (experimental) — stable promotion targeted at 154, CL open as of 2026-09-20 | CL 8197225 (merged 2026-08-12); flag record; CL 8256918 |
| Firefox | No signal | ChromeStatus API record vendor views; position requested at mozilla/standards-positions #1444 |
| Safari | Positive standards position (“Support”); not shipped | WebKit/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.
security and privacy
- No new network capability — the option bag changes how subprotocols are spelled, not what a page can connect to. The same protocol-validation and handshake algorithm of the living standard runs for every branch of the union.
- Web-compat, not security, is the open risk — code that today passes a non-iterable object (coerced to
"[object Object]", then rejected withSyntaxError) would begin connecting with no subprotocols. The spec PR review flagged this pattern in shipping libraries and asked for usage measurement before merging; Chromium's flag gating (and the abandoned revert, CL 8294944) reflect the same caution. - The extension point is where security options will land — Chromium's
targetAddressSpacemember (flag-gated in websocket_init.idl) exists to let pages opt in to Local Network Access restrictions for WebSocket connections to private networks; it is tracked as its own feature.
specifications
| Document | Status |
|---|---|
| whatwg/websockets PR #76 — Add WebSocketInit option to constructor | Open, 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 constructor | The originating proposal (2015–) and de-facto explainer; records implementer and user demand |
| WebSockets living standard | Host specification; does not yet contain WebSocketInit (0 occurrences, verified 2026-09-20) |
| blink-dev — Intent to Prototype: WebSocket constructor option bag | Posted 2026-08-05; tracking bug 542670554. No Intent-to-Ship thread is linked from the ChromeStatus record as of 2026-09-20 |
see also
- Chrome Platform Status — Add options bag to WebSocket constructor (API record)
- MDN — WebSocket() constructor (documents the classic two forms only; no
WebSocketInitcoverage, verified 2026-09-20) - Chrome Platform Status — Support targetAddressSpace option for WebSockets (the first real option to ride this extension point)
- WPT — option-bag.any.js (the upstreamed tests)
- Chrome Platform Showcase — interactive demos for this feature