Skip to content

Next.js Setup Guide

Works with App Router, Pages Router, and Server Actions.

Prerequisites

Setup

1. Run CLI Init

bash
npx trigger.dev@latest init

This will:

  1. Ask if you want to install the Trigger.dev MCP server
  2. Log you in if not already authenticated
  3. Ask you to select your Trigger.dev project
  4. Install SDK packages
  5. Create a /trigger directory with an example task
  6. Create trigger.config.ts in your project root

Install the "Hello World" example task when prompted.

2. Start the Dev Server

bash
npx trigger.dev@latest dev

Run this alongside your Next.js dev server. The CLI watches your /trigger directory and communicates with Trigger.dev.

Tip: Run both concurrently:

json
{
  "scripts": {
    "trigger:dev": "npx trigger.dev@latest dev",
    "dev": "npx concurrently --kill-others --names \"next,trigger\" \"next dev\" \"npm run trigger:dev\""
  }
}

3. Test in Dashboard

Visit the Test page in your Trigger.dev dashboard → select the Example task → click "Run test".

4. Set Secret Key

Add to .env.local (App Router) or .env (Pages Router):

bash
TRIGGER_SECRET_KEY=your-dev-secret-key

Get your key from the dashboard → API Keys → DEV secret key.

Triggering Tasks

App Router — Route Handler

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

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

App Router — Server Actions

ts
// app/api/actions.ts
"use server";
import type { helloWorldTask } from "@/trigger/example";
import { tasks } from "@trigger.dev/sdk";

export async function triggerHelloWorld() {
  const handle = await tasks.trigger<typeof helloWorldTask>(
    "hello-world",
    "James"
  );
  return { handle };
}
tsx
// app/page.tsx
"use client";
import { triggerHelloWorld } from "./actions";

export default function Home() {
  return (
    <button onClick={async () => await triggerHelloWorld()}>
      Trigger task
    </button>
  );
}

Pages Router — API Route

ts
// pages/api/hello-world.ts
import { tasks } from "@trigger.dev/sdk";
import type { NextApiRequest, NextApiResponse } from "next";

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const handle = await tasks.trigger("hello-world", "James");
  res.status(200).json(handle);
}

Sync Vercel Environment Variables (Optional)

ts
// 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()],
  },
});

Requires VERCEL_ACCESS_TOKEN and VERCEL_PROJECT_ID env vars.

Deploy

bash
npx trigger.dev@latest deploy

Troubleshooting

Next.js Build Fails in CI (Missing API Key)

Mark affected pages as dynamic:

ts
export const dynamic = "force-dynamic";

Revalidation from Tasks

To trigger ISR revalidation from a Trigger.dev task:

ts
// app/api/revalidate/path/route.ts
import { NextRequest, NextResponse } from "next/server";
import { revalidatePath } from "next/cache";

export async function POST(request: NextRequest) {
  const { path, secret } = await request.json();
  if (secret !== process.env.REVALIDATION_SECRET) {
    return NextResponse.json({ message: "Invalid secret" }, { status: 401 });
  }
  revalidatePath(path);
  return NextResponse.json({ revalidated: true });
}
ts
// trigger/revalidate-path.ts
import { logger, task } from "@trigger.dev/sdk";

export const revalidatePath = task({
  id: "revalidate-path",
  run: async (payload: { path: string }) => {
    const response = await fetch(`${process.env.NEXTJS_APP_URL}/api/revalidate/path`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        path: payload.path,
        secret: process.env.REVALIDATION_SECRET,
      }),
    });
    logger.log("Revalidation result", { ok: response.ok });
    return { success: response.ok };
  },
});

React Event Handlers

tsx
// Works:
<Button onClick={() => myTask.trigger(data)}>Trigger</Button>

// Doesn't work (method not bound):
<Button onClick={myTask.trigger}>Trigger</Button>

Built from official Trigger.dev documentation