--- title: React hooks sidebarTitle: React hooks description: Using the Trigger.dev v3 API from your React application. --- Our react hooks package provides a set of hooks that make it easy to interact with the Trigger.dev API from your React application, using our [frontend API](/frontend/overview). You can use these hooks to fetch runs, batches, and subscribe to real-time updates. ## Installation Install the `@trigger.dev/react-hooks` package in your project: ```bash npm npm add @trigger.dev/react-hooks ``` ```bash pnpm pnpm add @trigger.dev/react-hooks ``` ```bash yarn yarn install @trigger.dev/react-hooks ``` ## Authentication All hooks accept an optional last argument `options` that accepts an `accessToken` param, which should be a valid Public Access Token. Learn more about [generating tokens in the frontend guide](/frontend/overview). ```tsx import { useRealtimeRun } from "@trigger.dev/react-hooks"; export function MyComponent({ runId, publicAccessToken, }: { runId: string; publicAccessToken: string; }) { const { run, error } = useRealtimeRun(runId, { accessToken: publicAccessToken, // This is required baseURL: "https://your-trigger-dev-instance.com", // optional, only needed if you are self-hosting Trigger.dev }); // ... } ``` Alternatively, you can use our `TriggerAuthContext` provider ```tsx import { TriggerAuthContext } from "@trigger.dev/react-hooks"; export function SetupTrigger({ publicAccessToken }: { publicAccessToken: string }) { return ( ); } ``` Now children components can use the hooks to interact with the Trigger.dev API. If you are self-hosting Trigger.dev, you can provide the `baseURL` to the `TriggerAuthContext` provider. ```tsx import { TriggerAuthContext } from "@trigger.dev/react-hooks"; export function SetupTrigger({ publicAccessToken }: { publicAccessToken: string }) { return ( ); } ``` ### Next.js and client components If you are using Next.js with the App Router, you have to make sure the component that uses the `TriggerAuthContext` is a client component. So for example, the following code will not work: ```tsx app/page.tsx import { TriggerAuthContext } from "@trigger.dev/react-hooks"; export default function Page() { return ( ); } ``` That's because `Page` is a server component and the `TriggerAuthContext.Provider` uses client-only react code. To fix this, wrap the `TriggerAuthContext.Provider` in a client component: ```ts components/TriggerProvider.tsx "use client"; import { TriggerAuthContext } from "@trigger.dev/react-hooks"; export function TriggerProvider({ accessToken, children, }: { accessToken: string; children: React.ReactNode; }) { return ( {children} ); } ``` ### Passing the token to the frontend Techniques for passing the token to the frontend vary depending on your setup. Here are a few ways to do it for different setups: #### Next.js App Router If you are using Next.js with the App Router and you are triggering a task from a server action, you can use cookies to store and pass the token to the frontend. ```tsx actions/trigger.ts "use server"; import { tasks } from "@trigger.dev/sdk/v3"; import type { exampleTask } from "@/trigger/example"; import { redirect } from "next/navigation"; import { cookies } from "next/headers"; export async function startRun() { const handle = await tasks.trigger("example", { foo: "bar" }); // Set the auto-generated publicAccessToken in a cookie cookies().set("publicAccessToken", handle.publicAccessToken); // ✅ this token only has access to read this run redirect(`/runs/${handle.id}`); } ``` Then in the `/runs/[id].tsx` page, you can read the token from the cookie and pass it to the `TriggerProvider`. ```tsx pages/runs/[id].tsx import { TriggerProvider } from "@/components/TriggerProvider"; export default function RunPage({ params }: { params: { id: string } }) { const publicAccessToken = cookies().get("publicAccessToken"); return ( ); } ``` Instead of a cookie, you could also use a query parameter to pass the token to the frontend: ```tsx actions/trigger.ts import { tasks } from "@trigger.dev/sdk/v3"; import type { exampleTask } from "@/trigger/example"; import { redirect } from "next/navigation"; import { cookies } from "next/headers"; export async function startRun() { const handle = await tasks.trigger("example", { foo: "bar" }); redirect(`/runs/${handle.id}?publicAccessToken=${handle.publicAccessToken}`); } ``` And then in the `/runs/[id].tsx` page: ```tsx pages/runs/[id].tsx import { TriggerProvider } from "@/components/TriggerProvider"; export default function RunPage({ params, searchParams, }: { params: { id: string }; searchParams: { publicAccessToken: string }; }) { return ( ); } ``` Another alternative would be to use a server-side rendered page to fetch the token and pass it to the frontend: ```tsx pages/runs/[id].tsx import { TriggerProvider } from "@/components/TriggerProvider"; import { generatePublicAccessToken } from "@/trigger/auth"; export default async function RunPage({ params }: { params: { id: string } }) { // This will be executed on the server only const publicAccessToken = await generatePublicAccessToken(params.id); return ( ); } ``` ```tsx trigger/auth.ts import { auth } from "@trigger.dev/sdk/v3"; export async function generatePublicAccessToken(runId: string) { return auth.createPublicToken({ scopes: { read: { runs: [runId], }, }, expirationTime: "1h", }); } ``` ## SWR vs Realtime hooks We offer two "styles" of hooks: SWR and Realtime. The SWR hooks use the [swr](https://swr.vercel.app/) library to fetch data once and cache it. The Realtime hooks use [Trigger.dev realtime](/realtime) to subscribe to updates in real-time. It can be a little confusing which one to use because [swr](https://swr.vercel.app/) can also be configured to poll for updates. But because of rate-limits and the way the Trigger.dev API works, we recommend using the Realtime hooks for most use-cases. All hooks named `useRealtime*` are Realtime hooks, and all hooks named `use*` are SWR hooks. ## Realtime 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
Error: {error.message}
; return
Run: {run.id}
; } ``` 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(runId, { accessToken: publicAccessToken, }); if (error) return
Error: {error.message}
; // Now run.payload and run.output are correctly typed return
Run: {run.id}
; } ``` See our [Realtime documentation](/realtime) for more information about the type of the run object and more. ### 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
Error: {error.message}
; return (
{runs.map((run) => (
Run: {run.id}
))}
); } ``` 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(tag); if (error) return
Error: {error.message}
; // Now runs[i].payload and runs[i].output are correctly typed return (
{runs.map((run) => (
Run: {run.id}
))}
); } ``` 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(tag); if (error) return
Error: {error.message}
; // 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 (
{runs.map((run) => (
Run: {run.id}
))}
); } ``` See our [Realtime documentation](/realtime) for more information. ### 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
Error: {error.message}
; return (
{runs.map((run) => (
Run: {run.id}
))}
); } ``` See our [Realtime documentation](/realtime) for more information. ### useRealtimeRunWithStreams The `useRealtimeRunWithStreams` hook allows you to subscribe to a run by its ID and also receive any streams that are emitted by the task. See our [Realtime documentation](/realtime#streams) for more information about emitting streams from a task. ```tsx "use client"; // This is needed for Next.js App Router or other RSC frameworks import { useRealtimeRunWithStreams } from "@trigger.dev/react-hooks"; export function MyComponent({ runId, publicAccessToken, }: { runId: string; publicAccessToken: string; }) { const { run, streams, error } = useRealtimeRunWithStreams(runId, { accessToken: publicAccessToken, }); if (error) return
Error: {error.message}
; return (
Run: {run.id}
{Object.keys(streams).map((stream) => (
Stream: {stream}
))}
); } ``` You can provide the type of the streams to the `useRealtimeRunWithStreams` hook: ```tsx import { useRealtimeRunWithStreams } from "@trigger.dev/react-hooks"; import type { myTask } from "@/trigger/myTask"; type STREAMS = { openai: string; // this is the type of each "part" of the stream }; export function MyComponent({ runId, publicAccessToken, }: { runId: string; publicAccessToken: string; }) { const { run, streams, error } = useRealtimeRunWithStreams(runId, { accessToken: publicAccessToken, }); if (error) return
Error: {error.message}
; const text = streams.openai?.map((part) => part).join(""); return (
Run: {run.id}
{text}
); } ``` As you can see above, each stream is an array of the type you provided, keyed by the stream name. If instead of a pure text stream you have a stream of objects, you can provide the type of the object: ```tsx import type { TextStreamPart } from "ai"; import type { myTask } from "@/trigger/myTask"; type STREAMS = { openai: TextStreamPart<{}> }; export function MyComponent({ runId, publicAccessToken, }: { runId: string; publicAccessToken: string; }) { const { run, streams, error } = useRealtimeRunWithStreams(runId, { accessToken: publicAccessToken, }); if (error) return
Error: {error.message}
; const text = streams.openai ?.filter((stream) => stream.type === "text-delta") ?.map((part) => part.text) .join(""); return (
Run: {run.id}
{text}
); } ``` ### Common options #### 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
Error: {error.message}
; return
Run: {run.id}
; } ``` 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
Error: {error.message}
; return
Run: {run.id}
; } ``` 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). #### experimental_throttleInMs The `*withStreams` variants of the Realtime hooks accept an `experimental_throttleInMs` option to throttle the updates from the server. This can be useful if you are getting too many updates and want to reduce the number of updates. ```tsx import { useRealtimeRunsWithStreams } from "@trigger.dev/react-hooks"; export function MyComponent({ runId, publicAccessToken, }: { runId: string; publicAccessToken: string; }) { const { runs, error } = useRealtimeRunsWithStreams(tag, { accessToken: publicAccessToken, experimental_throttleInMs: 1000, // Throttle updates to once per second }); if (error) return
Error: {error.message}
; return (
{runs.map((run) => (
Run: {run.id}
))}
); } ``` ## SWR Hooks ### useRun The `useRun` hook allows you to fetch a run by its ID. ```tsx "use client"; // This is needed for Next.js App Router or other RSC frameworks import { useRun } from "@trigger.dev/react-hooks"; export function MyComponent({ runId }: { runId: string }) { const { run, error, isLoading } = useRun(runId); if (isLoading) return
Loading...
; if (error) return
Error: {error.message}
; return
Run: {run.id}
; } ``` The `run` object returned is the same as the [run object](/management/runs/retrieve) returned by the Trigger.dev API. To correctly type the run's payload and output, you can provide the type of your task to the `useRun` hook: ```tsx import { useRun } from "@trigger.dev/react-hooks"; import type { myTask } from "@/trigger/myTask"; export function MyComponent({ runId }: { runId: string }) { const { run, error, isLoading } = useRun(runId, { refreshInterval: 0, // Disable polling }); if (isLoading) return
Loading...
; if (error) return
Error: {error.message}
; // Now run.payload and run.output are correctly typed return
Run: {run.id}
; } ``` ### Common options You can pass the following options to the all SWR hooks: Revalidate the data when the window regains focus. Revalidate the data when the browser regains a network connection. Poll for updates at the specified interval (in milliseconds). Polling is not recommended for most use-cases. Use the Realtime hooks instead. ### Common return values An error object if an error occurred while fetching the data. A boolean indicating if the data is currently being fetched. A boolean indicating if the data is currently being revalidated. A boolean indicating if an error occurred while fetching the data. ## Trigger Hooks We provide a set of hooks that can be used to trigger tasks from your frontend application. You'll need to generate a Public Access Token with `write` permissions to use these hooks. See our [frontend guide](/frontend/overview#write-scopes) for more information. ### useTaskTrigger The `useTaskTrigger` hook allows you to trigger a task from your frontend application. ```tsx "use client"; // This is needed for Next.js App Router or other RSC frameworks import { useTaskTrigger } from "@trigger.dev/react-hooks"; import type { myTask } from "@/trigger/myTask"; export function MyComponent({ publicAccessToken }: { publicAccessToken: string }) { const { submit, handle, error, isLoading } = useTaskTrigger("my-task", { accessToken: publicAccessToken, }); if (error) { return
Error: {error.message}
; } if (handle) { return
Run ID: {handle.id}
; } return ( ); } ``` ### useRealtimeTaskTrigger The `useRealtimeTaskTrigger` hook allows you to trigger a task from your frontend application and then subscribe to the run in using Realtime: ```tsx "use client"; // This is needed for Next.js App Router or other RSC frameworks import { useRealtimeTaskTrigger } from "@trigger.dev/react-hooks"; import type { myTask } from "@/trigger/myTask"; export function MyComponent({ publicAccessToken }: { publicAccessToken: string }) { const { submit, run, error, isLoading } = useRealtimeTaskTrigger("my-task", { accessToken: publicAccessToken, }); if (error) { return
Error: {error.message}
; } // This is the realtime run object, which will automatically update when the run changes if (run) { return
Run ID: {run.id}
; } return ( ); } ``` ### useRealtimeTaskTriggerWithStreams The `useRealtimeTaskTriggerWithStreams` hook allows you to trigger a task from your frontend application and then subscribe to the run in using Realtime, and also receive any streams that are emitted by the task. ```tsx "use client"; // This is needed for Next.js App Router or other RSC frameworks import { useRealtimeTaskTriggerWithStreams } from "@trigger.dev/react-hooks"; import type { myTask } from "@/trigger/myTask"; type STREAMS = { openai: string; // this is the type of each "part" of the stream }; export function MyComponent({ publicAccessToken }: { publicAccessToken: string }) { const { submit, run, streams, error, isLoading } = useRealtimeTaskTriggerWithStreams< typeof myTask, STREAMS >("my-task", { accessToken: publicAccessToken, }); if (error) { return
Error: {error.message}
; } if (streams && run) { const text = streams.openai?.map((part) => part).join(""); return (
Run ID: {run.id}
{text}
); } return ( ); } ```