Skip to Content
MSW Dev Tool LogoMSW DEV TOOL
HomeDocs
  • Home
    • INTRODUCTION

    • Getting Started
    • How to Use
    • Roadmap
    • FEATURES

    • HTTP
    • WebSocket
    • Handler Table
    • Tools
    • Node CLI
    • Browser CLI
    • UI

    • Custom UI
    • EXAMPLES

    • Playground
  • INTRODUCTION

  • Getting Started
  • How to Use
  • Roadmap
  • FEATURES

  • HTTP
  • WebSocket
  • Handler Table
  • Tools
  • Node CLI
  • Browser CLI
  • UI

  • Custom UI
  • EXAMPLES

  • Playground

On This Page

  • Connect existing WebSocket handlers
  • Declare logical message branches when needed
  • Configure temporary listener responses
  • Configure a custom response
  • Use dynamic response templates
  • Verify real-time message states
  • Verify connection failures and recovery
  • Explore a temporary real-time flow
Question? Give us feedback 
DocsWebSocket

WebSocket Mocking Scenarios

Use WebSocket runtime controls to test real-time message behavior, connection recovery, and temporary endpoints without changing application code.

Connect existing WebSocket handlers

Import ws from @msw-dev-tool/core/msw so MSW Dev Tool can discover the endpoint and its message listeners.

When the listener does not need to branch by message payload, keep the whole message listener as one mock.

import { ws } from "@msw-dev-tool/core/msw"; const chat = ws.link("ws://localhost:8080/chat"); export const handlers = [ chat.addEventListener("connection", ({ client }) => { client.addEventListener("message", (event) => { client.send(`received: ${String(event.data)}`); }); }), ];

After the handler connects, the WebSocket panel lists its endpoint and discovered message listener.

Declare logical message branches when needed

Use logical message branches only when one listener handles several payload types and you need to manage the behavior for each type independently. Keep the existing message handling and response logic, then add mswDevTool as the third argument to addEventListener.

import { ws } from "@msw-dev-tool/core/msw"; type ChatMessage = | { type: "chat/join"; userId: string } | { type: "chat/message"; message: { text: string; sentAt: string } }; const parseChatMessage = (data: unknown): ChatMessage => JSON.parse(String(data)) as ChatMessage; const chat = ws.link("ws://localhost:8080/chat"); export const handlers = [ chat.addEventListener("connection", ({ client }) => { client.addEventListener( "message", (event) => { const message = parseChatMessage(event.data); switch (message.type) { case "chat/join": client.send( JSON.stringify({ type: "chat/joined", userId: message.userId }), ); break; case "chat/message": client.send( JSON.stringify({ type: "chat/message", message: message.message }), ); break; } }, { mswDevTool: { eventTypes: ["chat/join", "chat/message"], resolveEventType: (data) => parseChatMessage(data).type, }, }, ); }), ];
  • Use the first, normal mock when every message gets the same response or you do not need to manage behavior inside the handler separately.
  • Use the branching mock when one listener handles multiple payload types and each type needs independent behavior.
  • Declare only the type values you want to manage in eventTypes. resolveEventType receives event.data, not the full MessageEvent, and returns the type string.
  • Other listeners that do not need logical branches can remain normal message listeners without options. Message listeners on the same endpoint can each use different eventTypes and resolveEventType functions.

Logical branches support only WebSocket message listeners. Lifecycle events such as open, close, and error, batch payloads that contain multiple logical event types, and Socket.IO event protocols are not supported. Each message is classified as at most one logical event type. If resolveEventType throws or returns a type not listed in eventTypes, MSW Dev Tool does not apply branch control and runs the original listener.

Configure temporary listener responses

Temporary listeners start with the default Behavior. Their default response and the separate customResponse are independent values: default uses response, while custom response uses customResponse. Changing Behavior never replaces either value.

The temporary listener editor exposes the default Response and its optional delay (default 0 ms).

  • Repeat is configured in the separate Schedule section. Set the interval in milliseconds and the total number of repetitions, including the first response.
  • Configure Custom response independently from the listener row.
  • Use Infinity for an unbounded sequence. JSON and snapshots represent it as the string "Infinity", and its interval must be greater than zero.

Use a local test client for an unbounded sequence because it can produce a message flood. Stop it by updating or removing the listener, closing the client, or resetting the Dev Tool.

For example, this is the complete serializable listener configuration:

{ "behavior": { "preset": "default" }, "response": { "type": "send", "dataType": "string", "value": "temp response", "delay": 300, "repeat": { "interval": 500, "repetitions": 3 } }, "customResponse": { "type": "send", "dataType": "string", "value": "custom response", "delay": 100 } }

The first response is sent after delay; subsequent sends are separated by repeat.interval. Close responses run once after the delay and close the connection. Updating Behavior, either response, or the schedule cancels already-reserved responses before applying the new configuration.

Code-discovered listeners keep their original Behavior when no response is configured; temporary listeners with no response configured simply have nothing to send for the default Behavior.

Configure a custom response

The listener Behavior and its custom response data are separate settings. Saving configures the response only; select custom response in Behavior to apply it:

  1. Open the pencil button in the Custom response column and save a custom response.
  2. Select custom response in the Behavior control.
  3. Trigger the WebSocket message or connection flow in the application.

The editor supports two response actions:

  • Send sends one value when the listener receives a message.
    • String sends the value as text. JSON is allowed as plain text, for example {"type":"notification","ready":true}.
    • Blob sends bytes from a space-separated hexadecimal sequence, for example 89 50 4E 47 0D 0A 1A 0A. An optional metadata type can be supplied for the Blob.
    • ArrayBuffer sends the same hexadecimal byte representation as an ArrayBuffer.
  • Close closes the WebSocket after the listener receives a message. The close code and reason are optional. Codes must be 1000 or in the 3000–4999 range, and the reason is limited to 123 UTF-8 bytes.

Example configurations:

{ "type": "send", "dataType": "string", "value": "hello" }
{ "type": "send", "dataType": "Blob", "value": "89 50 4E 47 0D 0A 1A 0A", "metadata": { "type": "image/png" } }
{ "type": "close", "code": 4001, "reason": "Unauthorized" }

Save replaces the listener’s stored custom response. Closing the editor without saving leaves the previous configuration unchanged. Selecting custom response without a saved configuration produces Please configure a custom response before using this behavior.

Use dynamic response templates

String WebSocket responses can interpolate data from the incoming message with ${{event.data}} and dot paths such as ${{event.data.userId}}. Use templates in the temporary listener Response or a Custom response.

Set the response’s dataType to "string", then enter the response value as text. For example, if the application sends this message:

{ "userId": "user-7", "profile": { "displayName": "Ada" } }

Use this response value:

{ "userId": "${{event.data.userId}}", "name": "${{event.data.profile.displayName}}", "payload": ${{event.data}} }

The client receives the following string:

{ "userId": "user-7", "name": "Ada", "payload": {"userId":"user-7","profile":{"displayName":"Ada"}} }

For JSON object or array messages, MSW Dev Tool parses the incoming string so nested properties can be read. Plain text, malformed JSON, JSON primitives, and values with an unavailable path remain unchanged for the paths that cannot be read. An unsupported token remains in the response as its original ${{...}} text.

The rendered response is always a string. MSW Dev Tool does not parse the rendered response, choose the final data type for the application, validate generated JSON, or escape interpolated values for you. Make sure the receiving application handles parsing, data types, JSON validity, and escaping as needed. Templates do not execute JavaScript or call functions.

After saving a template, select default to use the listener’s Response, or select custom response to use its Custom response. Send a WebSocket message to verify the rendered value.

Verify real-time message states

Select a listener and choose the response behavior that fits the flow you are checking.

  • Send returns a chosen message for chat messages or notifications.
  • Echo returns the incoming message.
  • Send null returns null to verify nullable-message handling.
  • No reply leaves a sent message unanswered so you can verify waiting states.
  • Send sequence returns the configured sequence behavior to explore ordered real-time updates.
  • Custom response uses the saved custom response configuration. Choose Send or Close; Send supports String, Blob, and ArrayBuffer values, while Blob and ArrayBuffer values use space-separated hexadecimal bytes.

Use these controls to verify chat messages, notifications, pending indicators, and UI that depends on incoming data.

Verify connection failures and recovery

Use Close on a listener to close the connection after that listener receives its next message, then verify reconnect logic, offline UI, and connection-error recovery. Disable an endpoint or listener to stop its mock behavior while testing the corresponding recovery path.

Explore a temporary real-time flow

Add a temporary endpoint and listener in the WebSocket panel when you need to explore a new real-time screen without changing code. The listener defaults to default, so configure its Response and schedule when you want a delayed or repeated temporary payload. Configure Custom response separately, then select custom response to use it. Remove the temporary endpoint or listener, or reset the Dev Tool, when finished.

For a reproducible end-to-end check, start the example or another Node process with setupDevToolServer(), then create the endpoint and capture the exact endpoint.endpointId returned by ws-add-endpoint before adding the listener:

endpoint_id="$( msw-dev-tool --pid <pid> ws-add-endpoint \ --json '{"kind":"string","value":"ws://localhost:8080/preview"}' \ | jq -r '.endpoint.endpointId' )" listener_id="$( msw-dev-tool --pid <pid> ws-add-listener "$endpoint_id" --json '{ "behavior":{"preset":"default"}, "response":{"type":"send","dataType":"string","value":"temp response","delay":300,"repeat":{"interval":500,"repetitions":3}}, "customResponse":{"type":"send","dataType":"string","value":"custom response","delay":100} }' | jq -r '.listener.info.id' )" msw-dev-tool --pid <pid> ws-set-listener-behavior "$listener_id" --json '{"preset":"custom response"}'

Connect a real WebSocket client, record the delayed response count, update customResponse and Behavior, and query ws-list after each mutation. The same commands work through the Browser CLI with --cdp-url and --target; use Chrome DevTools MCP to inspect the selected tab and its WebSocket client. Delete the listener at the end to verify that pending responses stop.

Endpoints and listeners discovered from code can be enabled, disabled, and given a different behavior, but they cannot be deleted. Only temporary endpoints and listeners can be deleted.

Last updated on September 23, 2026
HTTPHandler Table

Powered by nextra

© 2026 The msw dev tool Project.