Quick Use
npm install inngest @inngest/realtime- In your function, call
await publish({ channel, topic, data })from the step context - In React,
useInngestSubscription({ channel, topics })to read the stream
Intro
Inngest Realtime ships logs and intermediate results from a running job to the browser via Server-Sent Events. Combined with the React hooks, you get progress bars and live status without polling. Best for: long AI generation tasks, batch processing, anything where users wait and need to see progress. Works with: Inngest Cloud + any frontend that supports EventSource. Setup time: 5 minutes.
Publish from a job
import { inngest } from "./client";
export const generateReport = inngest.createFunction(
{ id: "generate-report" },
{ event: "report.requested" },
async ({ event, step, publish }) => {
await publish({ channel: `run.${event.data.runId}`, topic: "status",
data: { stage: "fetching", percent: 10 }});
const data = await step.run("fetch", () => fetchReportData(event.data));
await publish({ channel: `run.${event.data.runId}`, topic: "status",
data: { stage: "processing", percent: 50 }});
const report = await step.run("generate", () => generateReportPDF(data));
await publish({ channel: `run.${event.data.runId}`, topic: "status",
data: { stage: "done", percent: 100, url: report.url }});
return report;
},
);Subscribe from React
import { useInngestSubscription } from "@inngest/realtime/hooks";
function ReportProgress({ runId }: { runId: string }) {
const { latest } = useInngestSubscription({
channel: `run.${runId}`,
topics: ["status"],
});
if (!latest) return <div>Starting…</div>;
return (
<div>
<p>{latest.data.stage}</p>
<Progress value={latest.data.percent} />
{latest.data.url && <a href={latest.data.url}>Download report</a>}
</div>
);
}Subscribe to all messages (history)
const { messages } = useInngestSubscription({ channel, topics });
// messages is a chronological array — render as a log
return (
<ol>
{messages.map((m, i) => (
<li key={i}>{m.data.stage} ({m.data.percent}%)</li>
))}
</ol>
);Auth tokens (no client secrets)
The frontend uses a short-lived subscription token issued from your server. The Inngest signing key never reaches the browser.
FAQ
Q: How is Realtime different from polling? A: SSE pushes the next message the moment it's published. Polling adds latency (the polling interval) and load (request per poll). Realtime is one persistent connection with sub-second updates.
Q: Does Realtime work outside the browser? A: Yes — any HTTP client that supports SSE works. The React hooks are optional convenience; you can subscribe from Vue / Svelte / vanilla JS / a server-side EventSource.
Q: Is it included in the free tier? A: Yes — Realtime is included in all Inngest Cloud tiers, including free. Self-hosted Inngest also supports Realtime via the same API.
Source & Thanks
Built by Inngest. Licensed under Apache-2.0.
Inngest Realtime docs — Active