Binary frames
Most apps send JSON over the wire. Some apps need binary: MessagePack or protobuf for compact payloads, audio chunks, image tiles, anything that benefits from skipping JSON parse cost. The manager treats the wire type as a generic parameter, so binary mode is fully type-safe and entirely opt-in.
ArrayBuffer round trip
Set binaryType: "arraybuffer" and parameterize
the manager with the binary wire types. The library plumbs
binaryType into the underlying
WebSocket instance so incoming frames arrive
as ArrayBuffer.
import { WebSocketManager } from "@luciodale/react-socket";
type TClientMsg = { type: "ping" } | { type: "frame"; bytes: number };
type TServerMsg = { type: "pong" } | { type: "frame"; bytes: number };
function encode(msg: TClientMsg): ArrayBuffer {
const json = JSON.stringify(msg);
const bytes = new TextEncoder().encode(json);
const ab = new ArrayBuffer(bytes.byteLength);
new Uint8Array(ab).set(bytes);
return ab;
}
function decode(raw: ArrayBuffer): TServerMsg {
return JSON.parse(new TextDecoder().decode(new Uint8Array(raw)));
}
const manager = new WebSocketManager<
TClientMsg,
TServerMsg,
"type",
ArrayBuffer, // wire type sent
ArrayBuffer // wire type received
>({
url: "wss://api.example.com/ws",
binaryType: "arraybuffer",
serialize: encode,
deserialize: decode,
});Blob mode
Blob mode works the same way, but synchronous deserialization is
awkward because Blob → bytes is async. Most apps prefer
arraybuffer unless you specifically want the
browser to defer materializing the bytes.
// Blobs are easier when you receive larger frames you don't need to
// process synchronously. The browser keeps the data on disk-or-memory
// until you await blob.text() / blob.arrayBuffer().
const manager = new WebSocketManager<
TClientMsg,
TServerMsg,
"type",
Blob,
Blob
>({
url: getWsUrl(),
binaryType: "blob",
serialize: (msg) => new Blob([JSON.stringify(msg)]),
deserialize: (raw) => {
// For Blob deserialization you usually pre-fetch metadata or stream
// the body separately. Sync deserialization is awkward; consider
// arraybuffer mode unless you specifically need Blob semantics.
throw new Error("implement async pre-decode in your bridge component");
},
});Mixed string + binary
A connection can carry both. Widen the wire generics to a union and
branch in serialize /
deserialize:
// String and binary on the same connection.
const manager = new WebSocketManager<
TClientMsg,
TServerMsg,
"type",
string | ArrayBuffer, // serialize can return either
string | ArrayBuffer // deserialize must accept either
>({
url: getWsUrl(),
binaryType: "arraybuffer",
serialize: (msg) =>
msg.type === "blob" ? encode(msg) : JSON.stringify(msg),
deserialize: (raw) =>
typeof raw === "string"
? JSON.parse(raw)
: decode(raw),
});MessagePack example
A common reason to reach for binary is replacing JSON with MessagePack
or protobuf. The encode/decode functions slot directly into
serialize / deserialize:
import { encode, decode } from "@msgpack/msgpack";
const manager = new WebSocketManager<
TClientMsg,
TServerMsg,
"type",
Uint8Array, // msgpack returns Uint8Array
ArrayBuffer
>({
url: getWsUrl(),
binaryType: "arraybuffer",
serialize: (msg) => encode(msg),
deserialize: (raw) => decode(new Uint8Array(raw)) as TServerMsg,
});Things that still work
- Discriminator narrowing on
useSocketEventworks on the deserialized object, not the raw bytes. - Subscriptions, ping/pong, in-flight ack tracking, optimistic updates — everything operates on
TClientMsg/TServerMsg, not the wire type. - Debug events expose the binary
rawfield, so the inspector still shows that a frame happened (even if it cannot pretty-print the bytes).
Testing binary
MockTransport's sentMessages
holds the raw wire payload. For binary, assert against the
ArrayBuffer directly. For string payloads, parse
with JSON.parse(transport.sentMessages.at(-1) as string);
for binary, decode with your own helper before comparing.
Next steps
- Configuration — full list of manager options including
binaryType - Backpressure — batching high-frequency binary frames into a single render
- Testing — asserting against binary
sentMessages