WorkflowsMay 7, 2026·3 min read

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.

Agent ready

This asset can be read and installed directly by agents

TokRepo exposes a universal CLI command, install contract, metadata JSON, adapter-aware plan, and raw content links so agents can judge fit, risk, and next actions.

Native · 98/100Policy: allow
Agent surface
Any MCP/CLI agent
Kind
Skill
Install
Single
Trust
Trust: New
Entrypoint
Asset
Universal CLI install command
npx tokrepo install 175edc4b-d637-4779-86ae-6e3a296a0465
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.


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

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

🙏

Source & Thanks

Built by Inngest. Licensed under Apache-2.0.

Inngest Realtime docs — Active

Discussion

Sign in to join the discussion.
No comments yet. Be the first to share your thoughts.

Related Assets