Skip to page content

useWebSocket composable
v2.34+

The useWebSocket() composable keeps a WebSocket connection alive from a component: it opens the socket, exposes the last message received as a reactive value, queues what you send until the socket is ready, reconnects with a backoff when the connection drops (and right away when the browser comes back online), can send a heartbeat, and closes the socket when the component gets destroyed.

NOTE

On the server-side of SSR or SSG modes, no socket gets created: socketStatus stays closed, sendSocketMessage() does nothing and no message ever arrives. The socket opens on the client once the component is mounted, so the status is closed before hydration too.

Outside of a component

The composable can also be called outside of setup(): in a boot file, a store or a plain module. There is no mount to wait for there, so the socket opens right away (unless lazy is set) and nothing closes it by itself: call closeSocket() when you are done. It releases everything the composable holds (the socket, the queued messages, the online/offline listeners and the watcher on a reactive url), and a later openSocket() sets it all up again.

Syntax

import { useWebSocket } from 'quasar'

setup () {
  const {
    socketStatus, socketData, socketError, sendSocketMessage, openSocket, closeSocket
  } = useWebSocket(
    url, // String, URL, or a ref/getter of one;
         // '/live' and 'https://...' forms are mapped to ws(s)
    {
      // all optional:

      lazy: true, // do not open the socket on mount (default: false);
                  // openSocket() or the first sendSocketMessage() does it

      protocols: ['chat'],        // the native sub-protocol(s) (default: none)
      binaryType: 'arraybuffer',  // 'blob' or 'arraybuffer' (default: 'blob')

      autoReconnect: {  // reopen a socket that closed on its own (default: true; false disables it)
        retries: 5,     // number of attempts (default: Infinity)
        delay: attempt => 500 * (attempt + 1) // ms, a number works too (default: 1s doubling up to 30s)
      },

      heartbeat: {         // send a message at a fixed interval while open (default: false; true for the defaults below)
        message: 'ping',   // what to send (default: 'ping')
        interval: 30000    // every X ms while the socket is open (default: 30000)
      },

      onOpen (evt) { // called each time the socket opens
        // ...
      },
      onMessage (data, evt) { // called with each message received
        // ...
      },
      onClose (evt, reason) { // called when the socket closes;
        // reason: 'programmatic' | 'unmount' | 'url' | 'remote'
      },
      onError (evt) { // called with the socket's "error" event
        // ...
      },
      onReconnect (attempt, delay) { // called when a reconnect gets scheduled
        // ...
      }
    }
  )

  // ...
}
function useWebSocket<Data = any>(
  url: MaybeRefOrGetter<string | URL>,
  options?: {
    lazy?: boolean
    protocols?: string | string[]
    binaryType?: 'blob' | 'arraybuffer'
    autoReconnect?:
      | boolean
      | {
          retries?: number
          delay?: number | ((attempt: number) => number)
        }
    heartbeat?:
      | boolean
      | {
          message?: string | ArrayBufferLike | Blob | ArrayBufferView
          interval?: number
        }
    onOpen?: (evt: Event) => void
    onMessage?: (data: Data, evt: MessageEvent<Data>) => void
    onClose?: (
      evt: CloseEvent,
      reason: 'programmatic' | 'unmount' | 'url' | 'remote'
    ) => void
    onError?: (evt: Event) => void
    onReconnect?: (attempt: number, delay: number) => void
  }
): {
  socketStatus: Ref<'closed' | 'connecting' | 'open'>
  socketData: ShallowRef<Data | null>
  socketError: ShallowRef<Event | null>
  sendSocketMessage: (
    message: string | ArrayBufferLike | Blob | ArrayBufferView
  ) => void
  openSocket: () => void
  closeSocket: (code?: number, reason?: string) => void
}

Lifecycle

The socket opens when the component is mounted (or right away, when the composable is used outside of a component) and is closed when the component gets destroyed. Set lazy: true for a socket that should wait for your openSocket() call (or your first sendSocketMessage(), which opens the socket by itself).

Each call of useWebSocket() manages one connection to one endpoint; for several sockets, call it several times.

closeSocket(code, reason) closes the connection with the native close code and reason, drops the queued messages and stops any reconnecting. The native constraints apply: the code is 1000 or one in the 3000 to 4999 range and the reason is at most 123 bytes of UTF-8; an invalid pair closes with the defaults instead. It is not final: a later openSocket() or sendSocketMessage() opens a fresh connection.

Once the component got destroyed, the composable is done: openSocket() and sendSocketMessage() do nothing anymore, so a late async callback cannot open a socket that nothing would close.

socketStatus is connecting from the moment the socket is requested until it is open, open while messages flow, and closed when it was never opened, when you closed it, or when reconnecting was given up. While waiting to reconnect the status is connecting too, as the composable is still working on it.

When the url is a ref or a getter and its value changes while the socket is wanted, the current connection is closed and a new one is opened to the new URL (a token in the query string, a different room).

The url does not have to be a ws:// or wss:// one: a relative URL ('/api/live') is resolved against the page and an http:// or https:// one is mapped to ws:// or wss://. A socket that follows the page’s own scheme this way can never be blocked as mixed content on an https page. The newest browsers accept such URLs natively; the composable does it for the older ones Quasar supports too.

Sending and receiving

sendSocketMessage(message) sends a String, Blob, ArrayBuffer or typed array. Messages sent while the socket is not open yet are queued and sent, in order, as soon as it opens; those sent from within onOpen go first, so a handshake (authentication, a subscription) reaches the server before the queued ones. Calling sendSocketMessage() on a closed socket opens it. closeSocket() drops whatever is still queued.

socketData holds the data of the last message received and socketError the last error event of the socket. The onMessage, onError, onOpen and onClose hooks get called in the same situations, so you do not need to watch the refs. Messages arrive as they were sent: parse them yourself (JSON.parse()) in onMessage if your protocol is JSON.

onClose(evt, reason) is called for every close, with the native CloseEvent (its code, reason and wasClean tell how the connection ended) and a second argument saying who asked for it: programmatic for your closeSocket() call, unmount when the component got destroyed, url when the socket was moved to a new URL, and remote when the socket closed on its own (the server closed it, or the connection dropped). The hook runs when the close event arrives, so for unmount the component is already gone by then. For remote it runs before any reconnect gets scheduled, so a closeSocket() call from within it keeps the socket closed.

Reconnecting

A socket that closes on its own (the server went away, the network dropped) is reopened after a delay: 1s, then doubling up to 30s, for as long as it takes. Tune it with autoReconnect: { retries, delay }, where delay is a number of ms or a function of the attempt index (starting at 0), or turn it off with autoReconnect: false. A successful connection resets the attempt count. onReconnect(attempt, delay) is called each time a reconnect gets scheduled, with the attempt number (starting at 1) and the ms to wait, so a “reconnecting…” notice can say how long; when the retries run out, socketStatus becomes closed.

No attempt is made while the browser reports being offline: the composable waits for the online event and reconnects right away when it fires (onReconnect(1, 0)), starting a fresh run of attempts, even when the retries had run out. closeSocket() ends all that: the socket stays closed until you call openSocket() again.

Heartbeat

Some proxies and load balancers drop a connection that stays silent for a while. heartbeat: true sends the String 'ping' every 30 seconds while the socket is open; set { message, interval } to match what your server expects. The heartbeat stops with the socket and starts over on each (re)connection.

Example

The example below talks to a public echo server, which greets each new connection then repeats everything it receives. The socket waits for your click (lazy); open it, close it, or turn your network off and on to see the status follow.

Basic


Content Security Policy

A socket URL must be allowed by the connect-src directive of your CSP.