React hooks / Next.js TypeScript SDK v4
In v4, React subscriptions use useRealtime from inngest/react together
with getClientSubscriptionToken() on the server.
Need the old useInngestSubscription() docs? See the archived
v3 docs
here.
useRealtime manages token refresh, reconnection, buffering, pause behavior,
and typed message access per topic. The typical Next.js flow is:
- Define a shared channel with
realtime.channel(). - Mint a scoped token in a server action or route handler.
- Pass the channel, topics, and token factory into
useRealtime().
Next.js example
app/actions.ts"use server";
import { getClientSubscriptionToken } from "inngest/react";
import { inngest } from "@/inngest/client";
import { helloChannel } from "@/inngest/channels";
export async function fetchRealtimeSubscriptionToken(runId: string) {
return getClientSubscriptionToken(inngest, {
channel: helloChannel({ runId }),
topics: ["logs"],
});
}
app/page.tsx"use client";
import { useRealtime } from "inngest/react";
import { helloChannel } from "@/inngest/channels";
import { fetchRealtimeSubscriptionToken } from "./actions";
export default function Home({ runId }: { runId: string }) {
const topics = ["logs"] as const;
const channel = helloChannel({ runId });
const {
connectionStatus,
runStatus,
messages,
error,
reset,
} = useRealtime({
channel,
topics,
token: () => fetchRealtimeSubscriptionToken(runId),
bufferInterval: 100,
});
return (
<div>
<p>Connection: {connectionStatus}</p>
<p>Run: {runStatus}</p>
<p>Latest log: {messages.byTopic.logs?.data}</p>
<p>Buffered messages: {messages.delta.length}</p>
{error && <p>{error.message}</p>}
<button onClick={() => reset()}>Reset</button>
</div>
);
}
What useRealtime gives you
connectionStatusfor websocket lifecycle staterunStatusfor function lifecycle statemessages.byTopicfor the latest typed message per topicmessages.allfor retained message historymessages.lastandmessages.deltafor incremental UI updateserrorandreset()for operational handling
Common options
enabledto delay the connection until you have arunIdbufferIntervalto batch UI updates for fast streamshistoryLimitto cap retained messagespauseOnHiddento pause when the tab is hiddenautoCloseOnTerminalto disconnect when the run finishes
Typed topic access
When you pass a channel instance and a literal topics array, TypeScript narrows
message payloads per topic:
const topics = ["status", "artifact"] as const;
const { messages } = useRealtime({
channel,
topics,
token: () => fetchRealtimeSubscriptionToken(runId),
});
messages.byTopic.status?.data.message;
messages.byTopic.artifact?.data.title;
for (const message of messages.delta) {
if (message.kind === "run") continue;
if (message.topic === "status") {
message.data.message;
}
}
Learn more
useRealtime API reference
See the full hook API including connection states, buffering, and reset semantics.
Explore patterns and examples
See complete examples including next-realtime-hooks and the TanStack Start
realtime demo.
Archived v3 hook docs
Reference the deprecated useInngestSubscription() docs if you still
maintain a v3 app.