# 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 (
);
}
```
### 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) => (
- {m.data.stage} ({m.data.percent}%)
))}
);
```
### 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 (
);
}
```
### 订阅所有消息(历史)
```tsx
const { messages } = useInngestSubscription({ channel, topics });
// messages 是按时间排好的数组,可以渲染成日志
return (
{messages.map((m, i) => (
- {m.data.stage} ({m.data.percent}%)
))}
);
```
### 鉴权(前端无密钥)
前端用从你服务器签发的短期订阅 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