Skip to main content

React Hooks

Setup

Wrap your application with RealtimeScope to provide the client to all hooks:

import { RealtimeClient } from "@armaanjain/courier-web-sdk";
import { RealtimeScope } from "@armaanjain/courier-web-sdk/react";

const client = new RealtimeClient({ endpoint: { ... } });
await client.connect();

function App() {
return (
<RealtimeScope client={client}>
<MyApp />
</RealtimeScope>
);
}

useLinkStatus

Track connection state and heartbeat health:

import { useLinkStatus } from "@armaanjain/courier-web-sdk/react";

function ConnectionBadge() {
const { linkState, heartbeatOk, isLive } = useLinkStatus();

return (
<div>
<span>State: {linkState}</span>
<span>Heartbeat: {heartbeatOk ? "OK" : "Degraded"}</span>
<span>{isLive ? "Online" : "Offline"}</span>
</div>
);
}

Return Type

FieldTypeDescription
linkStateLinkStateCurrent connection state
heartbeatOkbooleanLast heartbeat succeeded
isLivebooleanShorthand for linkState === LinkState.live

useTopicBind

Declaratively subscribe to a topic. Subscribes on mount, unsubscribes on unmount. The subscription is tracked by the ledger and survives reconnections.

import { useTopicBind } from "@armaanjain/courier-web-sdk/react";

function ChatRoom({ roomId }: { roomId: string }) {
const { bound, error } = useTopicBind(`chat/room/${roomId}`);

if (error) return <div>Failed to subscribe</div>;
if (!bound) return <div>Subscribing...</div>;
return <div>Subscribed to room {roomId}</div>;
}

Pass null to conditionally skip subscription:

const { bound } = useTopicBind(isReady ? `chat/room/${roomId}` : null);

Return Type

FieldTypeDescription
boundbooleantrue once broker acknowledges
errorError | nullNon-null if subscribe failed

useInbox

Listen for messages on a topic (or all topics):

import { useInbox } from "@armaanjain/courier-web-sdk/react";

function MessageLog({ roomId }: { roomId: string }) {
const [messages, setMessages] = useState<any[]>([]);

useInbox(`chat/room/${roomId}`, (topic, data) => {
setMessages((prev) => [...prev, data]);
});

return (
<ul>
{messages.map((msg, i) => (
<li key={i}>{JSON.stringify(msg)}</li>
))}
</ul>
);
}

Listen to all topics:

useInbox((topic, data) => {
console.log(`[${topic}]`, data);
});

useRealtimeClient

Direct access to the RealtimeClient instance:

import { useRealtimeClient } from "@armaanjain/courier-web-sdk/react";

function PublishButton() {
const client = useRealtimeClient();

const send = async () => {
await client.publish("chat/room/42", { text: "Hello!" });
};

return <button onClick={send}>Send</button>;
}

Complete Example

import { RealtimeClient, DeliveryMode } from "@armaanjain/courier-web-sdk";
import {
RealtimeScope,
useLinkStatus,
useTopicBind,
useInbox,
useRealtimeClient,
} from "@armaanjain/courier-web-sdk/react";
import { useState } from "react";

const client = new RealtimeClient({
endpoint: {
host: "broker.example.com",
port: 443,
scheme: "wss",
path: "/mqtt",
clientId: "react-app",
username: "user",
password: "token",
},
topicAudit: { enabled: true },
});

client.connect();

export default function App() {
return (
<RealtimeScope client={client}>
<Chat />
</RealtimeScope>
);
}

function Chat() {
const { isLive } = useLinkStatus();
const { bound } = useTopicBind("chat/room/general");
const client = useRealtimeClient();
const [messages, setMessages] = useState<string[]>([]);
const [input, setInput] = useState("");

useInbox("chat/room/general", (_, data: any) => {
setMessages((prev) => [...prev, data.text]);
});

const send = async () => {
if (input.trim()) {
await client.publish("chat/room/general", { text: input });
setInput("");
}
};

if (!isLive) return <p>Connecting...</p>;
if (!bound) return <p>Joining room...</p>;

return (
<div>
<ul>
{messages.map((m, i) => <li key={i}>{m}</li>)}
</ul>
<input value={input} onChange={(e) => setInput(e.target.value)} />
<button onClick={send}>Send</button>
</div>
);
}