Refactor high-complexity React components in Dify frontend. Use when `pnpm analyze-component...
npx skills add triggerdotdev/skills --skill "trigger-realtime"
Install specific skill from multi-skill repository
# Description
Subscribe to Trigger.dev task runs in real-time from frontend and backend. Use when building progress indicators, live dashboards, streaming AI/LLM responses, or React components that display task status.
# SKILL.md
name: trigger-realtime
description: Subscribe to Trigger.dev task runs in real-time from frontend and backend. Use when building progress indicators, live dashboards, streaming AI/LLM responses, or React components that display task status.
Trigger.dev Realtime
Subscribe to task runs and stream data in real-time from frontend and backend.
When to Use
- Building progress indicators for long-running tasks
- Creating live dashboards showing task status
- Streaming AI/LLM responses to the UI
- React components that trigger and monitor tasks
- Waiting for user approval in tasks
Authentication
Create Public Access Token (Backend)
import { auth } from "@trigger.dev/sdk";
// Read-only token for specific runs
const publicToken = await auth.createPublicToken({
scopes: {
read: {
runs: ["run_123"],
tasks: ["my-task"],
},
},
expirationTime: "1h",
});
// Pass this token to your frontend
Create Trigger Token (for frontend triggering)
const triggerToken = await auth.createTriggerPublicToken("my-task", {
expirationTime: "30m",
});
Backend Subscriptions
import { runs, tasks } from "@trigger.dev/sdk";
// Trigger and subscribe
const handle = await tasks.trigger("my-task", { data: "value" });
for await (const run of runs.subscribeToRun(handle.id)) {
console.log(`Status: ${run.status}`);
console.log(`Progress: ${run.metadata?.progress}`);
if (run.status === "COMPLETED") {
console.log("Output:", run.output);
break;
}
}
// Subscribe to tagged runs
for await (const run of runs.subscribeToRunsWithTag("user-123")) {
console.log(`Run ${run.id}: ${run.status}`);
}
// Subscribe to batch
for await (const run of runs.subscribeToBatch(batchId)) {
console.log(`Batch run ${run.id}: ${run.status}`);
}
React Hooks
Installation
npm add @trigger.dev/react-hooks
Trigger Task from React
"use client";
import { useRealtimeTaskTrigger } from "@trigger.dev/react-hooks";
import type { myTask } from "../trigger/tasks";
function TaskTrigger({ accessToken }: { accessToken: string }) {
const { submit, run, isLoading } = useRealtimeTaskTrigger<typeof myTask>(
"my-task",
{ accessToken }
);
return (
<div>
<button
onClick={() => submit({ data: "value" })}
disabled={isLoading}
>
Start Task
</button>
{run && (
<div>
<p>Status: {run.status}</p>
<p>Progress: {run.metadata?.progress}%</p>
{run.output && <p>Result: {JSON.stringify(run.output)}</p>}
</div>
)}
</div>
);
}
Subscribe to Existing Run
"use client";
import { useRealtimeRun } from "@trigger.dev/react-hooks";
import type { myTask } from "../trigger/tasks";
function RunStatus({ runId, accessToken }: { runId: string; accessToken: string }) {
const { run, error } = useRealtimeRun<typeof myTask>(runId, {
accessToken,
onComplete: (run) => {
console.log("Completed:", run.output);
},
});
if (error) return <div>Error: {error.message}</div>;
if (!run) return <div>Loading...</div>;
return (
<div>
<p>Status: {run.status}</p>
<p>Progress: {run.metadata?.progress || 0}%</p>
</div>
);
}
Subscribe to Tagged Runs
"use client";
import { useRealtimeRunsWithTag } from "@trigger.dev/react-hooks";
function UserTasks({ userId, accessToken }: { userId: string; accessToken: string }) {
const { runs } = useRealtimeRunsWithTag(`user-${userId}`, { accessToken });
return (
<ul>
{runs.map((run) => (
<li key={run.id}>{run.id}: {run.status}</li>
))}
</ul>
);
}
Realtime Streams (AI/LLM)
Define Stream (shared location)
// trigger/streams.ts
import { streams } from "@trigger.dev/sdk";
export const aiStream = streams.define<string>({
id: "ai-output",
});
Pipe Stream in Task
import { task } from "@trigger.dev/sdk";
import { aiStream } from "./streams";
export const streamingTask = task({
id: "streaming-task",
run: async (payload: { prompt: string }) => {
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();
},
});
Read Stream in React
"use client";
import { useRealtimeStream } from "@trigger.dev/react-hooks";
import { aiStream } from "../trigger/streams";
function AIResponse({ runId, accessToken }: { runId: string; accessToken: string }) {
const { parts, error } = useRealtimeStream(aiStream, runId, {
accessToken,
throttleInMs: 50,
});
if (error) return <div>Error: {error.message}</div>;
if (!parts) return <div>Waiting for response...</div>;
return <div>{parts.join("")}</div>;
}
Wait Tokens (Human-in-the-loop)
In Task
import { task, wait } from "@trigger.dev/sdk";
export const approvalTask = task({
id: "approval-task",
run: async (payload) => {
// Process initial data
const processed = await processData(payload);
// Wait for human approval
const approval = await wait.forToken<{ approved: boolean }>({
token: `approval-${payload.id}`,
timeoutInSeconds: 86400, // 24 hours
});
if (approval.approved) {
return await finalizeData(processed);
}
throw new Error("Not approved");
},
});
Complete Token from React
"use client";
import { useWaitToken } from "@trigger.dev/react-hooks";
function ApprovalButton({ tokenId, accessToken }: { tokenId: string; accessToken: string }) {
const { complete } = useWaitToken(tokenId, { accessToken });
return (
<div>
<button onClick={() => complete({ approved: true })}>
Approve
</button>
<button onClick={() => complete({ approved: false })}>
Reject
</button>
</div>
);
}
Run Object Properties
| Property | Description |
|---|---|
id |
Unique run identifier |
status |
QUEUED, EXECUTING, COMPLETED, FAILED, CANCELED |
payload |
Task input (typed) |
output |
Task result (typed, when completed) |
metadata |
Real-time updatable data |
createdAt |
Start timestamp |
costInCents |
Execution cost |
Best Practices
- Scope tokens narrowly β only grant necessary permissions
- Set expiration times β don't use long-lived tokens
- Use typed hooks β pass task types for proper inference
- Handle errors β always check for errors in hooks
- Throttle streams β use
throttleInMsto control re-renders
See references/realtime.md for complete documentation.
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.