# Trigger.dev Realtime (v4) **Real-time monitoring and updates for runs** ## Core Concepts Realtime allows you to: - Subscribe to run status changes, metadata updates, and streams - Build real-time dashboards and UI updates - Monitor task progress from frontend and backend ## Authentication ### Public Access Tokens ```ts import { auth } from "@trigger.dev/sdk"; // Read-only token for specific runs const publicToken = await auth.createPublicToken({ scopes: { read: { runs: ["run_123", "run_456"], tasks: ["my-task-1", "my-task-2"], }, }, expirationTime: "1h", // Default: 15 minutes }); ``` ### Trigger Tokens (Frontend only) ```ts // Single-use token for triggering tasks const triggerToken = await auth.createTriggerPublicToken("my-task", { expirationTime: "30m", }); ``` ## Backend Usage ### Subscribe to Runs ```ts import { runs, tasks } from "@trigger.dev/sdk"; // Trigger and subscribe const handle = await tasks.trigger("my-task", { data: "value" }); // Subscribe to specific run for await (const run of runs.subscribeToRun(handle.id)) { console.log(`Status: ${run.status}, Progress: ${run.metadata?.progress}`); if (run.status === "COMPLETED") break; } // Subscribe to runs with tag for await (const run of runs.subscribeToRunsWithTag("user-123")) { console.log(`Tagged run ${run.id}: ${run.status}`); } // Subscribe to batch for await (const run of runs.subscribeToBatch(batchId)) { console.log(`Batch run ${run.id}: ${run.status}`); } ``` ### Streams ```ts import { task, metadata } from "@trigger.dev/sdk"; // Task that streams data export type STREAMS = { openai: OpenAI.ChatCompletionChunk; }; export const streamingTask = task({ id: "streaming-task", run: async (payload) => { const completion = await openai.chat.completions.create({ model: "gpt-4", messages: [{ role: "user", content: payload.prompt }], stream: true, }); // Register stream const stream = await metadata.stream("openai", completion); let text = ""; for await (const chunk of stream) { text += chunk.choices[0]?.delta?.content || ""; } return { text }; }, }); // Subscribe to streams for await (const part of runs.subscribeToRun(runId).withStreams()) { switch (part.type) { case "run": console.log("Run update:", part.run.status); break; case "openai": console.log("Stream chunk:", part.chunk); break; } } ``` ## React Frontend Usage ### Installation ```bash npm add @trigger.dev/react-hooks ``` ### Triggering Tasks ```tsx "use client"; import { useTaskTrigger, useRealtimeTaskTrigger } from "@trigger.dev/react-hooks"; import type { myTask } from "../trigger/tasks"; function TriggerComponent({ accessToken }: { accessToken: string }) { // Basic trigger const { submit, handle, isLoading } = useTaskTrigger("my-task", { accessToken, }); // Trigger with realtime updates const { submit: realtimeSubmit, run, isLoading: isRealtimeLoading, } = useRealtimeTaskTrigger("my-task", { accessToken }); return (
{run &&
Status: {run.status}
}
); } ``` ### Subscribing to Runs ```tsx "use client"; import { useRealtimeRun, useRealtimeRunsWithTag } from "@trigger.dev/react-hooks"; import type { myTask } from "../trigger/tasks"; function SubscribeComponent({ runId, accessToken }: { runId: string; accessToken: string }) { // Subscribe to specific run const { run, error } = useRealtimeRun(runId, { accessToken, onComplete: (run) => { console.log("Task completed:", run.output); }, }); // Subscribe to tagged runs const { runs } = useRealtimeRunsWithTag("user-123", { accessToken }); if (error) return
Error: {error.message}
; if (!run) return
Loading...
; return (
Status: {run.status}
Progress: {run.metadata?.progress || 0}%
{run.output &&
Result: {JSON.stringify(run.output)}
}

Tagged Runs:

{runs.map((r) => (
{r.id}: {r.status}
))}
); } ``` ### Streams with React ```tsx "use client"; import { useRealtimeRunWithStreams } from "@trigger.dev/react-hooks"; import type { streamingTask, STREAMS } from "../trigger/tasks"; function StreamComponent({ runId, accessToken }: { runId: string; accessToken: string }) { const { run, streams } = useRealtimeRunWithStreams(runId, { accessToken, }); const text = streams.openai .filter((chunk) => chunk.choices[0]?.delta?.content) .map((chunk) => chunk.choices[0].delta.content) .join(""); return (
Status: {run?.status}
Streamed Text: {text}
); } ``` ### Wait Tokens ```tsx "use client"; import { useWaitToken } from "@trigger.dev/react-hooks"; function WaitTokenComponent({ tokenId, accessToken }: { tokenId: string; accessToken: string }) { const { complete } = useWaitToken(tokenId, { accessToken }); return ; } ``` ### SWR Hooks (Fetch Once) ```tsx "use client"; import { useRun } from "@trigger.dev/react-hooks"; import type { myTask } from "../trigger/tasks"; function SWRComponent({ runId, accessToken }: { runId: string; accessToken: string }) { const { run, error, isLoading } = useRun(runId, { accessToken, refreshInterval: 0, // Disable polling (recommended) }); if (isLoading) return
Loading...
; if (error) return
Error: {error.message}
; return
Run: {run?.status}
; } ``` ## Run Object Properties Key properties available in run subscriptions: - `id`: Unique run identifier - `status`: `QUEUED`, `EXECUTING`, `COMPLETED`, `FAILED`, `CANCELED`, etc. - `payload`: Task input data (typed) - `output`: Task result (typed, when completed) - `metadata`: Real-time updatable data - `createdAt`, `updatedAt`: Timestamps - `costInCents`: Execution cost ## Best Practices - **Use Realtime over SWR**: Recommended for most use cases due to rate limits - **Scope tokens properly**: Only grant necessary read/trigger permissions - **Handle errors**: Always check for errors in hooks and subscriptions - **Type safety**: Use task types for proper payload/output typing - **Cleanup subscriptions**: Backend subscriptions auto-complete, frontend hooks auto-cleanup