Skip to main content
This guide applies to both the App Router and Pages Router, as well as Server Actions.

Prerequisites

  • A Next.js project (v13 or later recommended)
  • A Trigger.dev account with a project created
  • Node.js 18 or later

Initial setup

1

Run the CLI init command

In the root of your Next.js project, run the following command to initialize Trigger.dev:
npx trigger.dev@latest init
This will create a trigger.config.ts file and an example task at src/trigger/example.ts.
2

Start the dev server

Run the Trigger.dev dev server alongside your Next.js app:
npx trigger.dev@latest dev
Run next dev and trigger.dev dev concurrently. You can use a tool like concurrently or simply open two terminal tabs.
3

Run a test

Open the Trigger.dev dashboard, navigate to your project, and click Test in the sidebar. Select the hello-world task and click Run test.
4

View your run

After triggering a test, click through to the run page to see logs, status, and the output of your task in real time.

Set your secret key locally

Set your TRIGGER_SECRET_KEY environment variable in your .env.local file (App Router) or .env file (Pages Router). This key authenticates requests from your Next.js app to Trigger.dev. Find your DEV secret key on the API Keys page of the dashboard: Find your DEV API key under API Keys in the Trigger.dev dashboard. For more details on key types, see the Introduction.

Triggering tasks from Next.js

1

Create a Route Handler

Add a Route Handler at app/api/hello-world/route.ts:
app/api/hello-world/route.ts
import type { helloWorldTask } from "@/trigger/example";
import { tasks } from "@trigger.dev/sdk";
import { NextResponse } from "next/server";

// tasks.trigger also works with the edge runtime:
// export const runtime = "edge";

export async function GET() {
  const handle = await tasks.trigger<typeof helloWorldTask>(
    "hello-world",
    "James"
  );

  return NextResponse.json(handle);
}
2

Test your route

Start both servers, then visit http://localhost:3000/api/hello-world in your browser. The task will be triggered and the run ID will be returned as JSON.

Add environment variables

Before deploying, add any environment variables your tasks need to the Trigger.dev dashboard under Environment Variables. Your TRIGGER_SECRET_KEY is added automatically when you deploy.

Deploying your task

Deploy your Trigger.dev tasks independently of your Next.js app:
npx trigger.dev@latest deploy

Automatically sync Vercel environment variables (optional)

If you deploy to Vercel, you can sync environment variables from your Vercel project to Trigger.dev automatically using the syncVercelEnvVars build extension.
You need to set VERCEL_ACCESS_TOKEN and VERCEL_PROJECT_ID environment variables (and optionally VERCEL_TEAM_ID for team projects). You can generate a token in your Vercel account settings.
trigger.config.ts
import { defineConfig } from "@trigger.dev/sdk";
import { syncVercelEnvVars } from "@trigger.dev/build/extensions/core";

export default defineConfig({
  project: "<project ref>",
  build: {
    extensions: [syncVercelEnvVars()],
  },
});

Troubleshooting

ISR revalidation from a Trigger.dev task

ISR revalidation purges the Next.js cache for a specific path. Because tasks run outside Next.js, you need to call a revalidation API route from your task. App Router handler (app/api/revalidate/path/route.ts):
app/api/revalidate/path/route.ts
import { NextRequest, NextResponse } from "next/server";
import { revalidatePath } from "next/cache";

export async function POST(request: NextRequest) {
  try {
    const { path, type, secret } = await request.json();

    if (secret !== process.env.REVALIDATION_SECRET) {
      return NextResponse.json({ message: "Invalid secret" }, { status: 401 });
    }

    if (!path) {
      return NextResponse.json({ message: "Path is required" }, { status: 400 });
    }

    revalidatePath(path, type);
    return NextResponse.json({ revalidated: true });
  } catch (err) {
    console.error("Error revalidating path:", err);
    return NextResponse.json({ message: "Error revalidating path" }, { status: 500 });
  }
}
Pages Router handler (pages/api/revalidate/path.ts):
pages/api/revalidate/path.ts
import type { NextApiRequest, NextApiResponse } from "next";

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  try {
    if (req.method !== "POST") {
      return res.status(405).json({ message: "Method not allowed" });
    }

    const { path, secret } = req.body;

    if (secret !== process.env.REVALIDATION_SECRET) {
      return res.status(401).json({ message: "Invalid secret" });
    }

    if (!path) {
      return res.status(400).json({ message: "Path is required" });
    }

    await res.revalidate(path);
    return res.json({ revalidated: true });
  } catch (err) {
    console.error("Error revalidating path:", err);
    return res.status(500).json({ message: "Error revalidating path" });
  }
}
Trigger.dev task that calls the handler:
trigger/revalidate-path.ts
import { logger, task } from "@trigger.dev/sdk";

const NEXTJS_APP_URL = process.env.NEXTJS_APP_URL;
const REVALIDATION_SECRET = process.env.REVALIDATION_SECRET;

export const revalidatePath = task({
  id: "revalidate-path",
  run: async (payload: { path: string }) => {
    const { path } = payload;

    const response = await fetch(`${NEXTJS_APP_URL}/api/revalidate/path`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        path: `${NEXTJS_APP_URL}/${path}`,
        secret: REVALIDATION_SECRET,
      }),
    });

    if (response.ok) {
      logger.log("Path revalidation successful", { path });
      return { success: true };
    }

    logger.error("Path revalidation failed", {
      path,
      statusCode: response.status,
      statusText: response.statusText,
    });
    return { success: false, error: `Revalidation failed with status ${response.status}` };
  },
});
Test this task in the dashboard with the payload:
{ "path": "blog" }

Missing API key error

If you see an authentication error when triggering tasks locally, check that TRIGGER_SECRET_KEY is set in your .env.local (App Router) or .env (Pages Router) file and that the value matches the DEV secret key in the dashboard.

Button onClick not working with Server Actions

Make sure your page or component file includes "use client"; at the top when using onClick handlers that call server actions. Server actions themselves must have "use server"; at the top.

Additional resources

Build extensions

Add Python, browsers, FFmpeg, and other system dependencies to your tasks

Deployment guide

Learn how to deploy your tasks to production

Build docs developers (and LLMs) love