Triggering docs and adding options to all trigger hooks (#1528)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@trigger.dev/react-hooks": patch
|
||||
---
|
||||
|
||||
Add trigger options to all trigger hooks
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash npm
|
||||
npm add @trigger.dev/react-hooks
|
||||
```
|
||||
|
||||
```bash pnpm
|
||||
pnpm add @trigger.dev/react-hooks
|
||||
```
|
||||
|
||||
```bash yarn
|
||||
yarn install @trigger.dev/react-hooks
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## 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 (
|
||||
<TriggerAuthContext.Provider value={{ accessToken: publicAccessToken }}>
|
||||
<MyComponent />
|
||||
</TriggerAuthContext.Provider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
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 (
|
||||
<TriggerAuthContext.Provider
|
||||
value={{
|
||||
accessToken: publicAccessToken,
|
||||
baseURL: "https://your-trigger-dev-instance.com",
|
||||
}}
|
||||
>
|
||||
<MyComponent />
|
||||
</TriggerAuthContext.Provider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 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 (
|
||||
<TriggerAuthContext.Provider value={{ accessToken: "your-access-token" }}>
|
||||
<MyComponent />
|
||||
</TriggerAuthContext.Provider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
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 (
|
||||
<TriggerAuthContext.Provider
|
||||
value={{
|
||||
accessToken,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</TriggerAuthContext.Provider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 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<typeof exampleTask>("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 (
|
||||
<TriggerProvider accessToken={publicAccessToken}>
|
||||
<RunDetails id={params.id} />
|
||||
</TriggerProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
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<typeof exampleTask>("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 (
|
||||
<TriggerProvider accessToken={searchParams.publicAccessToken}>
|
||||
<RunDetails id={params.id} />
|
||||
</TriggerProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Another alternative would be to use a server-side rendered page to fetch the token and pass it to the frontend:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```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 (
|
||||
<TriggerProvider accessToken={publicAccessToken}>
|
||||
<RunDetails id={params.id} />
|
||||
</TriggerProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
```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",
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## 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.
|
||||
|
||||
<Note>
|
||||
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.
|
||||
</Note>
|
||||
|
||||
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 <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
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<typeof myTask>(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
// Now run.payload and run.output are correctly typed
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
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 <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
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<typeof myTask>(tag);
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
// Now runs[i].payload and runs[i].output are correctly typed
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
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<typeof myTask1 | typeof myTask2>(tag);
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
// 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 (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
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 <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
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 <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Run: {run.id}</div>
|
||||
<div>
|
||||
{Object.keys(streams).map((stream) => (
|
||||
<div key={stream}>Stream: {stream}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
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<typeof myTask, STREAMS>(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
const text = streams.openai?.map((part) => part).join("");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Run: {run.id}</div>
|
||||
<div>{text}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
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<typeof myTask, STREAMS>(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
const text = streams.openai
|
||||
?.filter((stream) => stream.type === "text-delta")
|
||||
?.map((part) => part.text)
|
||||
.join("");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Run: {run.id}</div>
|
||||
<div>{text}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 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 <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
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 <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
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 <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 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 <div>Loading...</div>;
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
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<typeof myTask>(runId, {
|
||||
refreshInterval: 0, // Disable polling
|
||||
});
|
||||
|
||||
if (isLoading) return <div>Loading...</div>;
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
// Now run.payload and run.output are correctly typed
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
### Common options
|
||||
|
||||
You can pass the following options to the all SWR hooks:
|
||||
|
||||
<ParamField path="revalidateOnFocus" type="boolean">
|
||||
Revalidate the data when the window regains focus.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="revalidateOnReconnect" type="boolean">
|
||||
Revalidate the data when the browser regains a network connection.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="refreshInterval" type="number">
|
||||
Poll for updates at the specified interval (in milliseconds). Polling is not recommended for most
|
||||
use-cases. Use the Realtime hooks instead.
|
||||
</ParamField>
|
||||
|
||||
### Common return values
|
||||
|
||||
<ResponseField name="error" type="Error">
|
||||
An error object if an error occurred while fetching the data.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="isLoading" type="boolean">
|
||||
A boolean indicating if the data is currently being fetched.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="isValidating" type="boolean">
|
||||
A boolean indicating if the data is currently being revalidated.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="isError" type="boolean">
|
||||
A boolean indicating if an error occurred while fetching the data.
|
||||
</ResponseField>
|
||||
|
||||
## 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<typeof myTask>("my-task", {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return <div>Error: {error.message}</div>;
|
||||
}
|
||||
|
||||
if (handle) {
|
||||
return <div>Run ID: {handle.id}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<button onClick={() => submit({ foo: "bar" })} disabled={isLoading}>
|
||||
{isLoading ? "Loading..." : "Trigger Task"}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 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<typeof myTask>("my-task", {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return <div>Error: {error.message}</div>;
|
||||
}
|
||||
|
||||
// This is the realtime run object, which will automatically update when the run changes
|
||||
if (run) {
|
||||
return <div>Run ID: {run.id}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<button onClick={() => submit({ foo: "bar" })} disabled={isLoading}>
|
||||
{isLoading ? "Loading..." : "Trigger Task"}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 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 <div>Error: {error.message}</div>;
|
||||
}
|
||||
|
||||
if (streams && run) {
|
||||
const text = streams.openai?.map((part) => part).join("");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Run ID: {run.id}</div>
|
||||
<div>{text}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<button onClick={() => submit({ foo: "bar" })} disabled={isLoading}>
|
||||
{isLoading ? "Loading..." : "Trigger Task"}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
@@ -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:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash npm
|
||||
npm add @trigger.dev/react-hooks
|
||||
```
|
||||
|
||||
```bash pnpm
|
||||
pnpm add @trigger.dev/react-hooks
|
||||
```
|
||||
|
||||
```bash yarn
|
||||
yarn install @trigger.dev/react-hooks
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## 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 (
|
||||
<TriggerAuthContext.Provider value={{ accessToken: publicAccessToken }}>
|
||||
<MyComponent />
|
||||
</TriggerAuthContext.Provider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
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 (
|
||||
<TriggerAuthContext.Provider
|
||||
value={{
|
||||
accessToken: publicAccessToken,
|
||||
baseURL: "https://your-trigger-dev-instance.com",
|
||||
}}
|
||||
>
|
||||
<MyComponent />
|
||||
</TriggerAuthContext.Provider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 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 (
|
||||
<TriggerAuthContext.Provider value={{ accessToken: "your-access-token" }}>
|
||||
<MyComponent />
|
||||
</TriggerAuthContext.Provider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
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 (
|
||||
<TriggerAuthContext.Provider
|
||||
value={{
|
||||
accessToken,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</TriggerAuthContext.Provider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 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<typeof exampleTask>("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 (
|
||||
<TriggerProvider accessToken={publicAccessToken}>
|
||||
<RunDetails id={params.id} />
|
||||
</TriggerProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
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<typeof exampleTask>("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 (
|
||||
<TriggerProvider accessToken={searchParams.publicAccessToken}>
|
||||
<RunDetails id={params.id} />
|
||||
</TriggerProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Another alternative would be to use a server-side rendered page to fetch the token and pass it to the frontend:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```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 (
|
||||
<TriggerProvider accessToken={publicAccessToken}>
|
||||
<RunDetails id={params.id} />
|
||||
</TriggerProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
```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",
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## 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.
|
||||
|
||||
<Note>
|
||||
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.
|
||||
</Note>
|
||||
|
||||
## 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 <div>Loading...</div>;
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
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<typeof myTask>(runId, {
|
||||
refreshInterval: 0, // Disable polling
|
||||
});
|
||||
|
||||
if (isLoading) return <div>Loading...</div>;
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
// Now run.payload and run.output are correctly typed
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
### Common options
|
||||
|
||||
You can pass the following options to the all SWR hooks:
|
||||
|
||||
<ParamField path="revalidateOnFocus" type="boolean">
|
||||
Revalidate the data when the window regains focus.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="revalidateOnReconnect" type="boolean">
|
||||
Revalidate the data when the browser regains a network connection.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="refreshInterval" type="number">
|
||||
Poll for updates at the specified interval (in milliseconds). Polling is not recommended for most
|
||||
use-cases. Use the Realtime hooks instead.
|
||||
</ParamField>
|
||||
|
||||
### Common return values
|
||||
|
||||
<ResponseField name="error" type="Error">
|
||||
An error object if an error occurred while fetching the data.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="isLoading" type="boolean">
|
||||
A boolean indicating if the data is currently being fetched.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="isValidating" type="boolean">
|
||||
A boolean indicating if the data is currently being revalidated.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="isError" type="boolean">
|
||||
A boolean indicating if an error occurred while fetching the data.
|
||||
</ResponseField>
|
||||
|
||||
## 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.
|
||||
@@ -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 <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
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<typeof myTask>(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
// Now run.payload and run.output are correctly typed
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
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 <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
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<typeof myTask>(tag);
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
// Now runs[i].payload and runs[i].output are correctly typed
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
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<typeof myTask1 | typeof myTask2>(tag);
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
// 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 (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 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 <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
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 <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Run: {run.id}</div>
|
||||
<div>
|
||||
{Object.keys(streams).map((stream) => (
|
||||
<div key={stream}>Stream: {stream}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
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<typeof myTask, STREAMS>(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
const text = streams.openai?.map((part) => part).join("");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Run: {run.id}</div>
|
||||
<div>{text}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
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<typeof myTask, STREAMS>(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
const text = streams.openai
|
||||
?.filter((stream) => stream.type === "text-delta")
|
||||
?.map((part) => part.text)
|
||||
.join("");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Run: {run.id}</div>
|
||||
<div>{text}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 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 <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
### 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 <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
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 <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
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 <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
@@ -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:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash npm
|
||||
npm add @trigger.dev/react-hooks
|
||||
```
|
||||
|
||||
```bash pnpm
|
||||
pnpm add @trigger.dev/react-hooks
|
||||
```
|
||||
|
||||
```bash yarn
|
||||
yarn install @trigger.dev/react-hooks
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## 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!
|
||||
});
|
||||
```
|
||||
|
||||
<Note>
|
||||
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).
|
||||
</Note>
|
||||
|
||||
## 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<typeof myTask>("my-task", {
|
||||
accessToken: publicAccessToken, // 👈 this is the "trigger" token
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return <div>Error: {error.message}</div>;
|
||||
}
|
||||
|
||||
if (handle) {
|
||||
return <div>Run ID: {handle.id}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<button onClick={() => submit({ foo: "bar" })} disabled={isLoading}>
|
||||
{isLoading ? "Loading..." : "Trigger Task"}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
`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<typeof myTask>("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 <div>Error: {error.message}</div>;
|
||||
}
|
||||
|
||||
if (handle) {
|
||||
return <div>Run ID: {handle.id}</div>;
|
||||
}
|
||||
|
||||
if (realtimeError) {
|
||||
return <div>Error: {realtimeError.message}</div>;
|
||||
}
|
||||
|
||||
if (run) {
|
||||
return <div>Run ID: {run.id}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<button onClick={() => submit({ foo: "bar" })} disabled={isLoading}>
|
||||
{isLoading ? "Loading..." : "Trigger Task"}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
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<typeof myTask>("my-task", {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return <div>Error: {error.message}</div>;
|
||||
}
|
||||
|
||||
// This is the realtime run object, which will automatically update when the run changes
|
||||
if (run) {
|
||||
return <div>Run ID: {run.id}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<button onClick={() => submit({ foo: "bar" })} disabled={isLoading}>
|
||||
{isLoading ? "Loading..." : "Trigger Task"}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 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 <div>Error: {error.message}</div>;
|
||||
}
|
||||
|
||||
if (streams && run) {
|
||||
const text = streams.openai?.map((part) => part).join("");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Run ID: {run.id}</div>
|
||||
<div>{text}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<button onClick={() => submit({ foo: "bar" })} disabled={isLoading}>
|
||||
{isLoading ? "Loading..." : "Trigger Task"}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
+12
-1
@@ -110,6 +110,10 @@
|
||||
{
|
||||
"source": "/runs-and-attempts",
|
||||
"destination": "/runs"
|
||||
},
|
||||
{
|
||||
"source": "/frontend/react-hooks",
|
||||
"destination": "/frontend/react-hooks/overview"
|
||||
}
|
||||
],
|
||||
"anchors": [
|
||||
@@ -207,7 +211,14 @@
|
||||
"group": "Frontend usage",
|
||||
"pages": [
|
||||
"frontend/overview",
|
||||
"frontend/react-hooks"
|
||||
{
|
||||
"group": "React hooks",
|
||||
"pages": [
|
||||
"frontend/react-hooks/overview",
|
||||
"frontend/react-hooks/realtime",
|
||||
"frontend/react-hooks/triggering"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
+1
-1
@@ -699,7 +699,7 @@ export const childTask2 = task({
|
||||
## Triggering from your frontend
|
||||
|
||||
If you want to trigger a task directly from a frontend application, you can use our [React
|
||||
hooks](/frontend/react-hooks#trigger-hooks).
|
||||
hooks](/frontend/react-hooks/triggering).
|
||||
|
||||
## Options
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
makeIdempotencyKey,
|
||||
RunHandleFromTypes,
|
||||
stringifyIO,
|
||||
TriggerOptions,
|
||||
type TriggerOptions,
|
||||
} from "@trigger.dev/core/v3";
|
||||
import useSWRMutation from "swr/mutation";
|
||||
import { useApiClient, UseApiClientOptions } from "./useApiClient.js";
|
||||
@@ -118,7 +118,7 @@ export type RealtimeTriggerInstanceWithStreams<
|
||||
TTask extends AnyTask,
|
||||
TStreams extends Record<string, any> = Record<string, any>,
|
||||
> = UseRealtimeRunWithStreamsInstance<TTask, TStreams> & {
|
||||
submit: (payload: TaskPayload<TTask>) => void;
|
||||
submit: (payload: TaskPayload<TTask>, options?: TriggerOptions) => void;
|
||||
isLoading: boolean;
|
||||
handle?: RunHandleFromTypes<InferRunTypes<TTask>>;
|
||||
};
|
||||
@@ -166,7 +166,7 @@ export function useRealtimeTaskTriggerWithStreams<
|
||||
}
|
||||
|
||||
export type RealtimeTriggerInstance<TTask extends AnyTask> = UseRealtimeRunInstance<TTask> & {
|
||||
submit: (payload: TaskPayload<TTask>) => void;
|
||||
submit: (payload: TaskPayload<TTask>, options?: TriggerOptions) => void;
|
||||
isLoading: boolean;
|
||||
handle?: RunHandleFromTypes<InferRunTypes<TTask>>;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user