Skip to content

WebSocket, WebTransport & NDJSON ​

Each demo runs the real tanstack-fetch/live client in your browser against a mock server. No backend is needed. Guide: WebSocket, WebTransport & streams.

WebSocket: api.ws ​

  1. Click Connect, and send a message right away. It's queued until the socket opens.
  2. Click Drop connection to simulate a network drop (close code 1006).
  3. Watch it reconnect: getToken() runs again, so the URL gets a fresh token (demo-token-v2, …).

Live demo · WebSocket

useWs — auth + reconnect

Chat against a mock socket. Send before it opens (queued), then drop the connection: it reconnects and getToken() runs again, so the URL gets a fresh token.

status: disconnected

import { createFetch } from 'tanstack-fetch/live'
import { useWs } from 'tanstack-fetch/react'

const api = createFetch({ baseUrl, getToken })

const ChatRoom = ({ id }: { id: number }) => {
  const { data, status, send, close } = useWs<ChatMessage, { id: number }>('/rooms/:id', {
    client: api,
    params: { id },
    authIn: 'query', // browsers can't send WS headers
    reconnect: { retries: 3, delayMs: 600 },
  })

  return (
    <>
      <p>{status} · {data?.text}</p>
      <button onClick={() => send({ text: 'hi' })}>Send</button>
      <button onClick={close}>Close</button>
    </>
  )
}

WebTransport: api.transport ​

Game state arrives as HTTP/3 datagrams, about 16 per second. send() pushes input back on the same session.

Live demo · WebTransport

useWs({ transport: 'webtransport' })

Game state streams in as unreliable datagrams (newest value wins). Same API as api.ws: send input back with send().

status: disconnected

import { useWs } from 'tanstack-fetch/react'

const Game = () => {
  const { data: state, status, send } = useWs<GameState, { id: number }, Input>('/game/:id', {
    client: api,
    params: { id: 1 },
    authIn: 'query',
    transport: 'webtransport', // HTTP/3 datagrams
  })

  return (
    <Board dot={state} live={status === 'connected'}
      onBoost={() => send({ boost: 1.5 })} />
  )
}

NDJSON stream: api.stream ​

An LLM-style answer streamed one JSON line at a time, from a POST with a body and an Authorization header. Stop aborts the request through signal.

Live demo · NDJSON

api.stream + streamedQuery

A POST with a body and an Authorization header, read line by line. EventSource cannot do either.

status: disconnected

import {
  experimental_streamedQuery as streamedQuery,
  useQuery,
} from '@tanstack/react-query'

const Answer = ({ prompt }: { prompt: string }) => {
  const { data: tokens = [], fetchStatus, refetch } = useQuery({
    queryKey: ['chat', prompt],
    queryFn: streamedQuery({
      streamFn: ({ signal }) =>
        api.stream<Token>('/chat', { body: { prompt }, signal }),
    }),
    enabled: false,
  })

  return (
    <>
      <button onClick={() => refetch()}>Ask</button>
      <p>{tokens.map((t) => t.text).join('')}</p>
      {fetchStatus === 'fetching' && <span>streaming…</span>}
    </>
  )
}

Framework hooks ​

useWs gives you the same socket as reactive state in React, Vue, Solid, Svelte and Angular:

tsx
import { useWs } from 'tanstack-fetch/react'

const { data, status, send } = useWs<ChatMessage, { id: string }>('/rooms/:id', {
  client: api,
  params: { id },
  authIn: 'query',
  // transport: 'webtransport',
})

See the useWs section in each guide: React · Vue · Solid · Svelte · Angular.

MIT Licensed · Created by Mohammad Garmabi · Not an official TanStack package