# 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}`); } ``` ### Realtime Streams v2 (Recommended) ```ts import { streams, InferStreamType } from "@trigger.dev/sdk"; // 1. Define streams (shared location) export const aiStream = streams.define({ id: "ai-output", }); export type AIStreamPart = InferStreamType; // 2. Pipe from task 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, }); const { waitUntilComplete } = aiStream.pipe(completion); await waitUntilComplete(); }, }); // 3. Read from backend const stream = await aiStream.read(runId, { timeoutInSeconds: 300, startIndex: 0, // Resume from specific chunk }); for await (const chunk of stream) { console.log("Chunk:", chunk); // Fully typed } ``` Enable v2 by upgrading to 4.1.0 or later. ## 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}
))}
); } ``` ### Realtime Streams with React ```tsx "use client"; import { useRealtimeStream } from "@trigger.dev/react-hooks"; import { aiStream } from "../trigger/streams"; function StreamComponent({ runId, accessToken }: { runId: string; accessToken: string }) { // Pass defined stream directly for type safety const { parts, error } = useRealtimeStream(aiStream, runId, { accessToken, timeoutInSeconds: 300, throttleInMs: 50, // Control re-render frequency }); if (error) return
Error: {error.message}
; if (!parts) return
Loading...
; const text = parts.join(""); // parts is typed as AIStreamPart[] return
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