diff --git a/.changeset/nine-vans-perform.md b/.changeset/nine-vans-perform.md
new file mode 100644
index 0000000000..89049dd325
--- /dev/null
+++ b/.changeset/nine-vans-perform.md
@@ -0,0 +1,5 @@
+---
+"@trigger.dev/react-hooks": patch
+---
+
+Add trigger options to all trigger hooks
diff --git a/docs/frontend/overview.mdx b/docs/frontend/overview.mdx
index e972a34eca..ad0721e471 100644
--- a/docs/frontend/overview.mdx
+++ b/docs/frontend/overview.mdx
@@ -89,35 +89,6 @@ const publicToken = await auth.createPublicToken({
});
```
-### Write scopes
-
-You can also specify write scopes, which is required for triggering tasks from your frontend application:
-
-```ts
-const publicToken = await auth.createPublicToken({
- scopes: {
- write: {
- tasks: ["my-task-1", "my-task-2"],
- },
- },
-});
-```
-
-This will allow the token to trigger the specified tasks. `tasks` is the only write scope available at the moment.
-
-We **strongly** recommend creating short-lived tokens for write scopes, as they can be used to trigger tasks from your frontend application:
-
-```ts
-const publicToken = await auth.createPublicToken({
- scopes: {
- write: {
- tasks: ["my-task-1"], // ✅ this token can trigger this task
- },
- },
- expirationTime: "1m", // ✅ this token will expire after 1 minute
-});
-```
-
### Expiration
By default, Public Access Token's expire after 15 minutes. You can specify a different expiration time when creating a Public Access Token:
diff --git a/docs/frontend/react-hooks.mdx b/docs/frontend/react-hooks.mdx
deleted file mode 100644
index 3ce859db08..0000000000
--- a/docs/frontend/react-hooks.mdx
+++ /dev/null
@@ -1,797 +0,0 @@
----
-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
- );
-}
-```
-
-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
- );
-}
-```
-
-### 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
- );
- }
-
- return (
-
- );
-}
-```
diff --git a/docs/frontend/react-hooks/overview.mdx b/docs/frontend/react-hooks/overview.mdx
new file mode 100644
index 0000000000..da4286c95c
--- /dev/null
+++ b/docs/frontend/react-hooks/overview.mdx
@@ -0,0 +1,333 @@
+---
+title: Overview
+sidebarTitle: Overview
+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, and subscribe to real-time updates, and trigger tasks from your frontend application.
+
+## 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.
+
+
+## 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.
+
+
+## Realtime hooks
+
+See our [Realtime hooks documentation](/frontend/react-hooks/realtime) for more information.
+
+## Trigger Hooks
+
+See our [Trigger hooks documentation](/frontend/react-hooks/triggering) for more information.
diff --git a/docs/frontend/react-hooks/realtime.mdx b/docs/frontend/react-hooks/realtime.mdx
new file mode 100644
index 0000000000..b8f732e940
--- /dev/null
+++ b/docs/frontend/react-hooks/realtime.mdx
@@ -0,0 +1,391 @@
+---
+title: Realtime hooks
+sidebarTitle: Realtime
+description: Get live updates from the Trigger.dev API in your frontend application.
+---
+
+These hooks allow you to subscribe to runs, batches, and streams using [Trigger.dev realtime](/realtime). Before reading this guide:
+
+- Read our [Realtime documentation](/realtime) to understand how the Trigger.dev realtime API works.
+- Read how to [setup and authenticate](/frontend/overview) using the `@trigger.dev/react-hooks` package.
+
+## 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}
+ ))}
+
+ );
+}
+```
+
+### 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
+ );
+}
+```
+
+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
+ );
+}
+```
+
+## 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
Error: {error.message}
;
+
+ return
Run: {run.id}
;
+}
+```
+
+### 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}
+ ))}
+
+ );
+}
+```
diff --git a/docs/frontend/react-hooks/triggering.mdx b/docs/frontend/react-hooks/triggering.mdx
new file mode 100644
index 0000000000..9b00d0f295
--- /dev/null
+++ b/docs/frontend/react-hooks/triggering.mdx
@@ -0,0 +1,250 @@
+---
+title: Trigger hooks
+sidebarTitle: Triggering
+description: Triggering tasks from your frontend application.
+---
+
+We provide a set of hooks that can be used to trigger tasks from your frontend application.
+
+## Demo
+
+We've created a [Demo application](https://github.com/triggerdotdev/realtime-llm-battle) that demonstrates how to use our React hooks to trigger tasks in a Next.js application. The application uses the `@trigger.dev/react-hooks` package to trigger a task and subscribe to the run in real-time.
+
+## 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
+
+To authenticate a trigger hook, you must provide a special one-time use "trigger" token. These tokens are very similar to [Public Access Tokens](/frontend/overview#authentication), but they can only be used once to trigger a task. You can generate a trigger token using the `auth.createTriggerPublicToken` function in your backend code:
+
+```ts
+import { auth } from "@trigger.dev/sdk/v3";
+// Somewhere in your backend code
+const triggerToken = await auth.createTriggerPublicToken("my-task");
+```
+
+These tokens also expire, with the default expiration time being 15 minutes. You can specify a custom expiration time by passing a `expirationTime` parameter:
+
+```ts
+import { auth } from "@trigger.dev/sdk/v3";
+// Somewhere in your backend code
+const triggerToken = await auth.createTriggerPublicToken("my-task", {
+ expirationTime: "24hr",
+});
+```
+
+You can also pass multiple tasks to the `createTriggerPublicToken` function to create a token that can trigger multiple tasks:
+
+```ts
+import { auth } from "@trigger.dev/sdk/v3";
+// Somewhere in your backend code
+const triggerToken = await auth.createTriggerPublicToken(["my-task-1", "my-task-2"]);
+```
+
+You can also pass the `multipleUse` parameter to create a token that can be used multiple times:
+
+```ts
+import { auth } from "@trigger.dev/sdk/v3";
+
+// Somewhere in your backend code
+const triggerToken = await auth.createTriggerPublicToken("my-task", {
+ multipleUse: true, // ❌ Use this with caution!
+});
+```
+
+
+ After generating the trigger token in your backend, you must pass it to your frontend application.
+ We have a guide on how to do this in the [React hooks
+ overview](/frontend/react-hooks/overview#passing-the-token-to-the-frontend).
+
+
+## Hooks
+
+### 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";
+// 👆 This is the type of your task
+
+export function MyComponent({ publicAccessToken }: { publicAccessToken: string }) {
+ // pass the type of your task here 👇
+ const { submit, handle, error, isLoading } = useTaskTrigger("my-task", {
+ accessToken: publicAccessToken, // 👈 this is the "trigger" token
+ });
+
+ if (error) {
+ return
Error: {error.message}
;
+ }
+
+ if (handle) {
+ return
Run ID: {handle.id}
;
+ }
+
+ return (
+
+ );
+}
+```
+
+`useTaskTrigger` returns an object with the following properties:
+
+- `submit`: A function that triggers the task. It takes the payload of the task as an argument.
+- `handle`: The run handle object. This object contains the ID of the run that was triggered, along with a Public Access Token that can be used to access the run.
+- `isLoading`: A boolean that indicates whether the task is currently being triggered.
+- `error`: An error object that contains any errors that occurred while triggering the task.
+
+The `submit` function triggers the task with the specified payload. You can additionally pass an optional [options](/triggering#options) argument to the `submit` function:
+
+```tsx
+submit({ foo: "bar" }, { tags: ["tag1", "tag2"] });
+```
+
+#### Using the handle object
+
+You can use the `handle` object to initiate a subsequent [realtime hook](/frontend/react-hooks/realtime#userealtimerun) to subscribe to the run.
+
+```tsx
+"use client"; // This is needed for Next.js App Router or other RSC frameworks
+
+import { useTaskTrigger, useRealtimeRun } from "@trigger.dev/react-hooks";
+import type { myTask } from "@/trigger/myTask";
+// 👆 This is the type of your task
+
+export function MyComponent({ publicAccessToken }: { publicAccessToken: string }) {
+ // pass the type of your task here 👇
+ const { submit, handle, error, isLoading } = useTaskTrigger("my-task", {
+ accessToken: publicAccessToken, // 👈 this is the "trigger" token
+ });
+
+ // use the handle object to preserve type-safety 👇
+ const { run, error: realtimeError } = useRealtimeRun(handle, {
+ accessToken: handle?.publicAccessToken,
+ enabled: !!handle, // Only subscribe to the run if the handle is available
+ });
+
+ if (error) {
+ return
Error: {error.message}
;
+ }
+
+ if (handle) {
+ return
Run ID: {handle.id}
;
+ }
+
+ if (realtimeError) {
+ return
Error: {realtimeError.message}
;
+ }
+
+ if (run) {
+ return
Run ID: {run.id}
;
+ }
+
+ return (
+
+ );
+}
+```
+
+We've also created some additional hooks that allow you to trigger tasks and subscribe to the run in one step:
+
+### 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