React provider + hooks for the Hiyve Semantic Relay — high-frequency, fire-and-forget, room-scoped messaging. Connects over WebTransport (QUIC datagrams, preferred) with automatic WebSocket fallback, so it works in every current browser and on networks that block UDP.
npm install @hiyve/react-semantic-relay @hiyve/semantic-relay-client
import { SemanticRelayProvider, useRelayTopic } from '@hiyve/react-semantic-relay';
function App() {
return (
<SemanticRelayProvider
url="https://semantic.hiyve.tv:4443/relay"
roomId="lesson-abc"
userId="alice@example.com"
token={jwtFromBackend}
serverCertificateHashes={certHashes}
>
<Instruments />
</SemanticRelayProvider>
);
}
function Instruments() {
const { publish, subscribe, isConnected } = useRelayTopic<{ midi: number; on: boolean }>(
'instrument-notes',
);
useEffect(() => subscribe(({ payload, userId }) => {
console.log(`${userId} played`, payload);
}), [subscribe]);
return (
<button disabled={!isConnected} onClick={() => publish({ midi: 60, on: true })}>
Play C
</button>
);
}
roomId + userId.serverCertificateHashes) — browsers refuse to connect over WebTransport without this when not using a public CA. The WebSocket fallback has no hash-pinning mechanism and requires the relay's TCP listener to serve a CA-trusted certificate.react ^18, @hiyve/semantic-relay-client.<SemanticRelayProvider>Provides a relay connection to descendants. Reconnects automatically when connection identity (url / roomId / userId / token) changes.
| Prop | Type | Required | Description |
|---|---|---|---|
url |
string |
yes | Base WebTransport URL. |
roomId |
string |
yes | Room identifier. |
userId |
string |
yes | Sender identity attached to every published message. |
token |
string |
yes | Short-lived auth token minted by your backend. |
serverCertificateHashes |
RelayCertHash[] |
no | SHA-256 certificate hashes for self-signed dev certs. See WebTransport docs for derivation. |
disabled |
boolean |
no | When true, the provider does not connect. |
onError |
(error: Error) => void |
no | Called on connection or transport errors. |
onConnectionChange |
(state: RelayConnectionState) => void |
no | Called whenever the connection state changes. |
onDebug |
(message: string) => void |
no | Optional debug callback for diagnostic output. |
maxSeenMessages |
number |
no | Cap for the deduplication ring buffer. |
pruneCount |
number |
no | Number of entries pruned when the ring buffer is full. |
retryDelaysMs |
number[] |
no | Custom backoff schedule for reconnection attempts. |
transport |
'auto' | 'webtransport' | 'websocket' |
no | Transport selection policy (default 'auto': WebTransport first, WebSocket on failure or absence). |
wsBufferedAmountHighWater |
number |
no | WebSocket congestion threshold in bytes (default 16 KB); under congestion, outbound messages coalesce latest-wins per topic. |
useSemanticRelay()Returns { client, state, session, isConnected, error, transportKind }. Use this for lower-level access — most consumers only need useRelayTopic. transportKind reports which transport the live connection uses ('webtransport' or 'websocket'), or null before connect.
useRelayTopic<T>(topic)Returns { publish, subscribe, isConnected }. The subscribe callback returns an unsubscribe function suitable for a useEffect cleanup.
useRelaySubProtocol<T>(options)Reliable per-message pub/sub on a topic — a drop-in replacement for the WebRTC data-channel sendDataMessage / 'data-message' listener pattern. On top of useRelayTopic it adds self-message filtering, bounded message dedup, transparent chunking for payloads above the datagram cap, and per-message acknowledgement with automatic retry.
const { publish, isConnected } = useRelaySubProtocol<{ x: number; y: number }>({
topic: 'myapp.cursor.move',
localUserId,
onMessage: (payload, meta) => applyRemotePoint(payload, meta.senderId),
onError: (err) => console.warn('sub-protocol:', err.message),
});
publish({ x: 0.5, y: 0.5 }); // acked + retried by default
publish({ x, y }, { requireAck: false }); // fire-and-forget (high-rate signals)
publish(edit, { ackFrom: authorUserId }); // targeted: only the author's ack counts
Options: topic, localUserId, onMessage(payload, meta), onError(err), dedupWindow (default 200), ignoreOwnMessages (default true), defaultRequireAck (default true).
Per-publish options (PublishOptions):
| Option | Type | Description |
|---|---|---|
requireAck |
boolean |
Override the hook's default ack policy for one message. false = fire-and-forget (no retry). |
ackFrom |
string |
Targeted ack: only an ack from this user id clears the retry — a bystander's ack can't mask a lost delivery to the one peer that must receive the message (e.g. an authoritative writer). Implies requireAck: true unless explicitly disabled. Must exactly match the target's relay user id. |
When a message exhausts its retry budget without the required ack, onError fires with a descriptive Error.
| Type | Description |
|---|---|
SemanticRelayProviderProps |
Props accepted by <SemanticRelayProvider>. |
UseSemanticRelayResult |
Return shape of useSemanticRelay(). |
UseRelayTopicResult<T> |
Return shape of useRelayTopic<T>(). |
RelayConnectionState |
Connection state reported by the provider ('idle' | 'connecting' | 'connected' | 'error' | 'closed'). |
RelayMessage<T> |
Shape of a received message: { topic, payload, userId, ... }. |
RelaySubscribeHandler<T> |
Callback signature for subscribe. |
UseRelaySubProtocolOptions<T> / UseRelaySubProtocolResult<T> |
Options / return shape of useRelaySubProtocol<T>(). |
PublishOptions |
Per-publish options for useRelaySubProtocol (requireAck, ackFrom). |
RelaySubProtocolMessageMeta |
Meta passed to onMessage (senderId, messageId, timestamp). |
RelayTransportKind |
'webtransport' | 'websocket' — active transport, see useSemanticRelay(). |
SessionInfo |
Session metadata returned by the relay (e.g. server-stamped session id). |
| Export | Description |
|---|---|
isRelaySupported() |
true when at least one transport is available in this runtime (effectively always in browsers; false under SSR). |
isWebTransportSupported() |
true when the preferred WebTransport API is present. Presence does not guarantee connectivity — selection happens at connect time. |
isWebSocketSupported() |
true when the WebSocket API is present. |
MIT
@hiyve/react-semantic-relay— React provider + hooks for the Hiyve Semantic Relay (WebTransport datagram service).Wraps
@hiyve/semantic-relay-clientwith a context provider that owns the client lifecycle, plus a topic-scoped pub/sub hook for everyday use.Example