December 4, 2024

Trigger from the frontend

Trigger.dev now supports triggering tasks from the frontend.

Introducing new React hooks for triggering Trigger.dev tasks directly from your frontend applications. Here's what's new:

  • The @trigger.dev/react-hooks has been updated to include new "trigger" hooks that allow developers to execute tasks directly from their frontend applications.
  • Built-in security through one-time use "trigger" tokens ensures safe task execution, with customizable expiration times.
  • Integrates with Realtime API to make it easy to trigger tasks and receive updates in real-time.

This allows you to simplify your backend code to go from this:

Backend Trigger

To this:

Frontend Trigger

Demo

We built a demo application to showcase how you can use the new hooks to trigger tasks directly from your frontend application and then receive updates in real-time:

In this demo:

  • A Next.js application that uses the new @trigger.dev/react-hooks to trigger a task.
  • A background task that sends a prompt to two different LLM models using the AI SDK and streams their responses back to the frontend in real-time.
  • The background task then evaluates the responses and sends the results back to the frontend in real-time, declaring a winner.

You can find the source code for this demo application here.

Features

One time use "trigger" tokens

We've added a new type of Public Access Token that can be used to trigger tasks directly from your frontend applications. These tokens are one-time use and can be configured to expire after a certain amount of time, as well as be restricted to specific tasks:


import { auth } from "@trigger.dev/sdk/v3";
// This should happen in your backend
const triggerToken = await auth.createTriggerPublicToken("my-task");
// Now pass your triggerToken to your frontend
// ...

By default, these tokens expire after 15 minutes, but you can customize the expiration time:


const triggerToken = await auth.createTriggerPublicToken("my-task", {
expirationTime: "24hr",
});

See the docs for more information.

useTaskTrigger hook

If you just want to trigger a task and don't need to listen for updates, you can use the useTaskTrigger hook:


"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 TriggerTask({ token }: { token: string }) {
// pass the type of your task here 👇
const { submit, handle, error, isLoading } = useTaskTrigger<typeof myTask>(
"my-task",
{
accessToken: token, // 👈 this is the "trigger" token
}
);
if (error) {
return <div>Error: {error.message}</div>;
}
if (handle) {
return <div>Run ID: {handle.id}</div>;
}
// submit with a typesafe payload 👇
return (
<button onClick={() => submit({ foo: "bar" })} disabled={isLoading}>
{isLoading ? "Loading..." : "Trigger Task"}
</button>
);
}

useRealtimeTaskTrigger

If you want to trigger a task and listen for updates in real-time, you can use the useRealtimeTaskTrigger hook:


"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 TriggerRealtimeTask({ token }: { token: string }) {
const { submit, run, error, isLoading } = useRealtimeTaskTrigger<
typeof myTask
>("my-task", {
accessToken: token,
});
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

If you want to trigger a task and listen for updates in real-time, including the ability to listen to streams, you can use the useRealtimeTaskTriggerWithStreams hook:


"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>
);
}

For more on how to use these hooks, see the docs.

Using one-time use tokens in other contexts

One-time use tokens can be used in any context where you need to trigger a task without exposing your API key, not just in frontend applications. For example, imagine you have a CLI tool that needs to trigger a task:


import { auth, tasks, runs } from "@trigger.dev/sdk/v3";
// This would get generated in your backend and passed to your CLI tool
const triggerToken = await auth.createTriggerPublicToken("create-project");
// In your CLI tool, authenticate with the trigger token
await auth.withAuth({ accessToken: triggerToken }, async () => {
const handle = await tasks.trigger("create-project", {
name: "My Project",
description: "A new project",
});
// Now authenticate with the PAT that has access to the run
await auth.withAuth({ accessToken: handle.publicAccessToken }, async () => {
// Now subscribe to the run
for await (const run of runs.subscribe(handle.id)) {
// Do something with the run
console.log(run);
}
// The run is complete
});
});

Multiple-use single use tokens

If you need to trigger a task multiple times with the same token, you can use the multipleUse option:


const triggerToken = await auth.createTriggerPublicToken("my-task", {
multipleUse: true, // 👈 this allows the token to be used multiple times
});

Just be aware that this is less secure than one-time use tokens, as the token can be used multiple times and will only be invalidated when it expires.

Getting started

Head over to our React hooks documentation to get started with the new hooks.

As always, we're here to help if you have any questions or need assistance. You can reach out to us on Discord or X.

Ready to start building?

Build and deploy your first task in 3 minutes.

Get started now
,