# Inngest Realtime — Stream Job Progress to UI in Browser > Inngest Realtime streams job logs, metadata, and step results from background functions to the browser via SSE. React hooks make progress UIs trivial. ## Install Copy the content below into your project: ## Quick Use 1. `npm install inngest @inngest/realtime` 2. In your function, call `await publish({ channel, topic, data })` from the step context 3. 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 ```typescript 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 ```tsx import { useInngestSubscription } from "@inngest/realtime/hooks"; function ReportProgress({ runId }: { runId: string }) { const { latest } = useInngestSubscription({ channel: `run.${runId}`, topics: ["status"], }); if (!latest) return
Starting…
; return (

{latest.data.stage}

{latest.data.url && Download report}
); } ``` ### Subscribe to all messages (history) ```tsx const { messages } = useInngestSubscription({ channel, topics }); // messages is a chronological array — render as a log return (
    {messages.map((m, i) => (
  1. {m.data.stage} ({m.data.percent}%)
  2. ))}
); ``` ### 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](https://github.com/inngest). Licensed under Apache-2.0. > > [Inngest Realtime docs](https://www.inngest.com/docs/features/realtime) — Active --- ## 快速使用 1. `npm install inngest @inngest/realtime` 2. 在 function 里从 step 上下文调 `await publish({ channel, topic, data })` 3. React 用 `useInngestSubscription({ channel, topics })` 读流 --- ## 简介 Inngest Realtime 通过 Server-Sent Events 把运行中任务的日志和中间结果流到浏览器。配合 React hook,进度条和实时状态不用轮询就能搭。适合长 AI 生成任务、批处理、任何用户等待且需要看进度的场景。需要 Inngest Cloud + 任何支持 EventSource 的前端。装机时间 5 分钟。 --- ### 从任务发布 ```typescript 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; }, ); ``` ### 在 React 里订阅 ```tsx import { useInngestSubscription } from "@inngest/realtime/hooks"; function ReportProgress({ runId }: { runId: string }) { const { latest } = useInngestSubscription({ channel: `run.${runId}`, topics: ["status"], }); if (!latest) return
Starting…
; return (

{latest.data.stage}

{latest.data.url && Download report}
); } ``` ### 订阅所有消息(历史) ```tsx const { messages } = useInngestSubscription({ channel, topics }); // messages 是按时间排好的数组,可以渲染成日志 return (
    {messages.map((m, i) => (
  1. {m.data.stage} ({m.data.percent}%)
  2. ))}
); ``` ### 鉴权(前端无密钥) 前端用从你服务器签发的短期订阅 token。Inngest 签名密钥永远不到浏览器。 --- ### FAQ **Q: Realtime 跟轮询啥区别?** A: SSE 在消息发布时立即推送。轮询有延迟(轮询间隔)和负载(每次都发请求)。Realtime 是一个持久连接,亚秒级更新。 **Q: 浏览器之外能用 Realtime 吗?** A: 能 —— 任何支持 SSE 的 HTTP 客户端都行。React hook 是可选的便捷封装,Vue / Svelte / 原生 JS / 服务端 EventSource 都能订阅。 **Q: 免费档包含吗?** A: 包含 —— Realtime 在所有 Inngest Cloud 档(含免费档)都可用。自托管 Inngest 也通过同 API 支持 Realtime。 --- ## 来源与感谢 > Built by [Inngest](https://github.com/inngest). Licensed under Apache-2.0. > > [Inngest Realtime docs](https://www.inngest.com/docs/features/realtime) — Active --- Source: https://tokrepo.com/en/workflows/inngest-realtime-stream-job-progress-to-ui-in-browser Author: Inngest