chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,674 @@
|
||||
---
|
||||
title: "Run updates in React"
|
||||
sidebarTitle: "Run updates"
|
||||
description: "Build progress bars, status indicators, and live dashboards by subscribing to background task updates from React components."
|
||||
---
|
||||
|
||||
**Subscribe to a run and your component re-renders whenever its status, metadata, or tags change.** Build progress bars, deployment monitors, or any UI that needs to reflect what a background task is doing right now.
|
||||
|
||||
For streaming continuous data (AI tokens, file chunks), see [Streaming](/realtime/react-hooks/streams) instead.
|
||||
|
||||
## Trigger + subscribe combo hooks
|
||||
|
||||
Trigger a task and immediately subscribe to its run. Details in the [triggering](/realtime/react-hooks/triggering) section.
|
||||
|
||||
- **[`useRealtimeTaskTrigger`](/realtime/react-hooks/triggering#userealtimetasktrigger)** - Trigger a task and subscribe to the run
|
||||
- **[`useRealtimeTaskTriggerWithStreams`](/realtime/react-hooks/triggering#userealtimetasktriggerwithstreams)** - Trigger a task and subscribe to both run updates and streams
|
||||
|
||||
## Subscribe hooks
|
||||
|
||||
### useRealtimeRun
|
||||
|
||||
The `useRealtimeRun` hook allows you to subscribe to a run by its ID.
|
||||
|
||||
```tsx
|
||||
"use client"; // This is needed for Next.js App Router or other RSC frameworks
|
||||
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
To correctly type the run's payload and output, you can provide the type of your task to the `useRealtimeRun` hook:
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
import type { myTask } from "@/trigger/myTask";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun<typeof myTask>(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
// Now run.payload and run.output are correctly typed
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
You can supply an `onComplete` callback to the `useRealtimeRun` hook to be called when the run is completed or errored. This is useful if you want to perform some action when the run is completed, like navigating to a different page or showing a notification.
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
onComplete: (run, error) => {
|
||||
console.log("Run completed", run);
|
||||
},
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
When you only need run status (for example, a progress bar or completion badge), you can omit large fields like `payload` and `output` by passing `skipColumns`. This reduces the data sent over the wire and avoids issues such as "Large HTTP Payload" warnings in tools like Sentry.
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function RunStatusBadge({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
skipColumns: ["payload", "output"],
|
||||
});
|
||||
|
||||
if (error) return <span>Error</span>;
|
||||
if (!run) return <span>Loading…</span>;
|
||||
|
||||
return <span>Status: {run.status}</span>;
|
||||
}
|
||||
```
|
||||
|
||||
You can skip any of: `payload`, `output`, `metadata`, `startedAt`, `delayUntil`, `queuedAt`, `expiredAt`, `completedAt`, `number`, `isTest`, `usageDurationMs`, `costInCents`, `baseCostInCents`, `ttl`, `payloadType`, `outputType`, `runTags`, `error`. The `useRealtimeRunsWithTag` hook also accepts a `skipColumns` option in the same way.
|
||||
|
||||
See our [run object reference](/realtime/run-object) for the complete schema and [How it Works documentation](/realtime/how-it-works) for more technical details.
|
||||
|
||||
### useRealtimeRunsWithTag
|
||||
|
||||
The `useRealtimeRunsWithTag` hook allows you to subscribe to multiple runs with a specific tag.
|
||||
|
||||
```tsx
|
||||
"use client"; // This is needed for Next.js App Router or other RSC frameworks
|
||||
|
||||
import { useRealtimeRunsWithTag } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({ tag }: { tag: string }) {
|
||||
const { runs, error } = useRealtimeRunsWithTag(tag);
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
To correctly type the runs payload and output, you can provide the type of your task to the `useRealtimeRunsWithTag` hook:
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRunsWithTag } from "@trigger.dev/react-hooks";
|
||||
import type { myTask } from "@/trigger/myTask";
|
||||
|
||||
export function MyComponent({ tag }: { tag: string }) {
|
||||
const { runs, error } = useRealtimeRunsWithTag<typeof myTask>(tag);
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
// Now runs[i].payload and runs[i].output are correctly typed
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
If `useRealtimeRunsWithTag` could return multiple different types of tasks, you can pass a union of all the task types to the hook:
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRunsWithTag } from "@trigger.dev/react-hooks";
|
||||
import type { myTask1, myTask2 } from "@/trigger/myTasks";
|
||||
|
||||
export function MyComponent({ tag }: { tag: string }) {
|
||||
const { runs, error } = useRealtimeRunsWithTag<typeof myTask1 | typeof myTask2>(tag);
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
// You can narrow down the type of the run based on the taskIdentifier
|
||||
for (const run of runs) {
|
||||
if (run.taskIdentifier === "my-task-1") {
|
||||
// run is correctly typed as myTask1
|
||||
} else if (run.taskIdentifier === "my-task-2") {
|
||||
// run is correctly typed as myTask2
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### useRealtimeBatch
|
||||
|
||||
The `useRealtimeBatch` hook allows you to subscribe to a batch of runs by its the batch ID.
|
||||
|
||||
```tsx
|
||||
"use client"; // This is needed for Next.js App Router or other RSC frameworks
|
||||
|
||||
import { useRealtimeBatch } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({ batchId }: { batchId: string }) {
|
||||
const { runs, error } = useRealtimeBatch(batchId);
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
See our [Realtime documentation](/realtime) for more information.
|
||||
|
||||
## Using metadata to show progress in your UI
|
||||
|
||||
All realtime hooks automatically include metadata updates. Whenever your task updates metadata using `metadata.set()`, `metadata.append()`, or other metadata methods, your component will re-render with the updated data.
|
||||
|
||||
<Note>To learn how to write tasks using metadata, see our [metadata](/runs/metadata) guide.</Note>
|
||||
|
||||
### Progress monitoring
|
||||
|
||||
This example demonstrates how to create a progress monitor component that can be used to display the progress of a run:
|
||||
|
||||
```tsx
|
||||
"use client"; // This is needed for Next.js App Router or other RSC frameworks
|
||||
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function ProgressMonitor({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, error, isLoading } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (isLoading) return <div>Loading run...</div>;
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
if (!run) return <div>Run not found</div>;
|
||||
|
||||
const progress = run.metadata?.progress as
|
||||
| {
|
||||
current: number;
|
||||
total: number;
|
||||
percentage: number;
|
||||
currentItem: string;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h3>Run Status: {run.status}</h3>
|
||||
<p>Run ID: {run.id}</p>
|
||||
</div>
|
||||
|
||||
{progress && (
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span>Progress</span>
|
||||
<span>{progress.percentage}%</span>
|
||||
</div>
|
||||
<div className="w-full bg-gray-200 rounded-full h-2">
|
||||
<div
|
||||
className="bg-blue-600 h-2 rounded-full transition-all duration-300"
|
||||
style={{ width: `${progress.percentage}%` }}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600">
|
||||
Processing: {progress.currentItem} ({progress.current}/{progress.total})
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Reusable progress bar
|
||||
|
||||
This example demonstrates how to create a reusable progress bar component that can be used to display the percentage progress of a run:
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
interface ProgressBarProps {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export function ProgressBar({ runId, publicAccessToken, title }: ProgressBarProps) {
|
||||
const { run } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
const progress = run?.metadata?.progress as
|
||||
| {
|
||||
current?: number;
|
||||
total?: number;
|
||||
percentage?: number;
|
||||
currentItem?: string;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
const percentage = progress?.percentage ?? 0;
|
||||
const isComplete = run?.status === "COMPLETED";
|
||||
const isFailed = run?.status === "FAILED";
|
||||
|
||||
return (
|
||||
<div className="w-full space-y-2">
|
||||
{title && <h4 className="font-medium">{title}</h4>}
|
||||
|
||||
<div className="w-full bg-gray-200 rounded-full h-3">
|
||||
<div
|
||||
className={`h-3 rounded-full transition-all duration-500 ${
|
||||
isFailed ? "bg-red-500" : isComplete ? "bg-green-500" : "bg-blue-500"
|
||||
}`}
|
||||
style={{ width: `${percentage}%` }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between text-sm text-gray-600">
|
||||
<span>
|
||||
{progress?.current && progress?.total
|
||||
? `${progress.current}/${progress.total} items`
|
||||
: "Processing..."}
|
||||
</span>
|
||||
<span>{percentage}%</span>
|
||||
</div>
|
||||
|
||||
{progress?.currentItem && (
|
||||
<p className="text-sm text-gray-500 truncate">Current: {progress.currentItem}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Status indicator with logs
|
||||
|
||||
This example demonstrates how to create a status indicator component that can be used to display the status of a run, and also logs that are emitted by the task:
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
interface StatusIndicatorProps {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}
|
||||
|
||||
export function StatusIndicator({ runId, publicAccessToken }: StatusIndicatorProps) {
|
||||
const { run } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
const status = run?.metadata?.status as string | undefined;
|
||||
const logs = run?.metadata?.logs as string[] | undefined;
|
||||
|
||||
const getStatusColor = (status: string | undefined) => {
|
||||
switch (status) {
|
||||
case "completed":
|
||||
return "text-green-600 bg-green-100";
|
||||
case "failed":
|
||||
return "text-red-600 bg-red-100";
|
||||
case "running":
|
||||
return "text-blue-600 bg-blue-100";
|
||||
default:
|
||||
return "text-gray-600 bg-gray-100";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<span className={`px-3 py-1 rounded-full text-sm font-medium ${getStatusColor(status)}`}>
|
||||
{status || run?.status || "Unknown"}
|
||||
</span>
|
||||
<span className="text-sm text-gray-500">Run {run?.id}</span>
|
||||
</div>
|
||||
|
||||
{logs && logs.length > 0 && (
|
||||
<div className="bg-gray-50 rounded-lg p-4">
|
||||
<h4 className="font-medium mb-2">Logs</h4>
|
||||
<div className="space-y-1 max-h-48 overflow-y-auto">
|
||||
{logs.map((log, index) => (
|
||||
<div key={index} className="text-sm text-gray-700 font-mono">
|
||||
{log}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Multi-stage deployment monitor
|
||||
|
||||
This example demonstrates how to create a multi-stage deployment monitor component that can be used to display the progress of a deployment:
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
interface DeploymentMonitorProps {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}
|
||||
|
||||
const DEPLOYMENT_STAGES = [
|
||||
"initializing",
|
||||
"building",
|
||||
"testing",
|
||||
"deploying",
|
||||
"verifying",
|
||||
"completed",
|
||||
] as const;
|
||||
|
||||
export function DeploymentMonitor({ runId, publicAccessToken }: DeploymentMonitorProps) {
|
||||
const { run } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
const status = run?.metadata?.status as string | undefined;
|
||||
const logs = run?.metadata?.logs as string[] | undefined;
|
||||
const currentStageIndex = DEPLOYMENT_STAGES.indexOf(status as any);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<h3 className="text-lg font-semibold">Deployment Progress</h3>
|
||||
|
||||
{/* Stage indicators */}
|
||||
<div className="space-y-4">
|
||||
{DEPLOYMENT_STAGES.map((stage, index) => {
|
||||
const isActive = currentStageIndex === index;
|
||||
const isCompleted = currentStageIndex > index;
|
||||
const isFailed = run?.status === "FAILED" && currentStageIndex === index;
|
||||
|
||||
return (
|
||||
<div key={stage} className="flex items-center space-x-3">
|
||||
<div
|
||||
className={`w-6 h-6 rounded-full flex items-center justify-center text-sm font-medium ${
|
||||
isFailed
|
||||
? "bg-red-500 text-white"
|
||||
: isCompleted
|
||||
? "bg-green-500 text-white"
|
||||
: isActive
|
||||
? "bg-blue-500 text-white"
|
||||
: "bg-gray-200 text-gray-600"
|
||||
}`}
|
||||
>
|
||||
{isCompleted ? "✓" : index + 1}
|
||||
</div>
|
||||
<span
|
||||
className={`capitalize ${
|
||||
isActive
|
||||
? "font-medium text-blue-600"
|
||||
: isCompleted
|
||||
? "text-green-600"
|
||||
: isFailed
|
||||
? "text-red-600"
|
||||
: "text-gray-500"
|
||||
}`}
|
||||
>
|
||||
{stage}
|
||||
</span>
|
||||
{isActive && (
|
||||
<div className="animate-spin w-4 h-4 border-2 border-blue-500 border-t-transparent rounded-full" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Recent logs */}
|
||||
{logs && logs.length > 0 && (
|
||||
<div className="bg-black text-green-400 rounded-lg p-4 font-mono text-sm">
|
||||
<div className="space-y-1 max-h-32 overflow-y-auto">
|
||||
{logs.slice(-5).map((log, index) => (
|
||||
<div key={index}>
|
||||
<span className="text-gray-500">$ </span>
|
||||
{log}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Type safety
|
||||
|
||||
Define TypeScript interfaces for your metadata to get full type safety:
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
interface TaskMetadata {
|
||||
progress?: {
|
||||
current: number;
|
||||
total: number;
|
||||
percentage: number;
|
||||
currentItem: string;
|
||||
};
|
||||
status?: "initializing" | "processing" | "completed" | "failed";
|
||||
user?: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
logs?: string[];
|
||||
}
|
||||
|
||||
export function TypedMetadataComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
// Type-safe metadata access
|
||||
const metadata = run?.metadata as TaskMetadata | undefined;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{metadata?.progress && <p>Progress: {metadata.progress.percentage}%</p>}
|
||||
|
||||
{metadata?.user && (
|
||||
<p>
|
||||
User: {metadata.user.name} ({metadata.user.id})
|
||||
</p>
|
||||
)}
|
||||
|
||||
{metadata?.status && <p>Status: {metadata.status}</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Common options
|
||||
|
||||
### accessToken & baseURL
|
||||
|
||||
You can pass the `accessToken` option to the Realtime hooks to authenticate the subscription.
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
baseURL: "https://my-self-hosted-trigger.com", // Optional if you are using a self-hosted Trigger.dev instance
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
### enabled
|
||||
|
||||
You can pass the `enabled` option to the Realtime hooks to enable or disable the subscription.
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
enabled,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
enabled: boolean;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
enabled,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
This allows you to conditionally disable using the hook based on some state.
|
||||
|
||||
### id
|
||||
|
||||
You can pass the `id` option to the Realtime hooks to change the ID of the subscription.
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
id,
|
||||
runId,
|
||||
publicAccessToken,
|
||||
enabled,
|
||||
}: {
|
||||
id: string;
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
enabled: boolean;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
enabled,
|
||||
id,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
This allows you to change the ID of the subscription based on some state. Passing in a different ID will unsubscribe from the current subscription and subscribe to the new one (and remove any cached data).
|
||||
|
||||
## Frequently asked questions
|
||||
|
||||
### How do I show a progress bar for a background task in React?
|
||||
|
||||
Use `metadata.set()` inside your task to update a progress value, then read it with `useRealtimeRun` in your component. The hook re-renders your component on every metadata change. See [Using metadata to show progress](#using-metadata-to-show-progress-in-your-ui) above for a complete example.
|
||||
|
||||
### What's the difference between run updates and streaming?
|
||||
|
||||
Run updates (this page) give you **run state**: status, metadata, and tags. They're for progress bars, status badges, and dashboards. [Streaming](/realtime/react-hooks/streams) gives you **continuous data** like AI tokens or file chunks. Use run updates for "how far along is my task?" and streaming for "show me the output as it generates."
|
||||
|
||||
### Can I subscribe to multiple runs at once?
|
||||
|
||||
Yes. Use `useRealtimeRunsWithTag` to subscribe to all runs with a specific tag (e.g., `user:123`), or `useRealtimeBatch` for all runs in a batch. Each yields an array of run objects that update in real time.
|
||||
|
||||
### Do I need to set up polling or WebSockets?
|
||||
|
||||
No. The hooks handle the connection automatically using Trigger.dev's Realtime infrastructure (built on [Electric SQL](/realtime/how-it-works)). Just pass a run ID and an access token.
|
||||
Reference in New Issue
Block a user