Getting started
Install with Next
Passback adds a small button to your Next.js app while you develop. Click it, click anything on the page, and write what should change. The comment goes to the coding agent you already have open. In React code (.jsx and .tsx files) it carries the file and line the element comes from.
Before you start
- A Next.js 15.3 or 16 app that uses the App Router (an
appfolder, orsrc/app). - App Router only: the Pages Router is not supported.
- Node 22 or later.
- One of Claude Code, Codex or Cursor, opened in the project.
Install
In your app's folder, add the two packages:
npm i -D passback @passback/nextSet the project up:
npx passback init --applyStart the dev server as usual (
npm run dev), or restart it if it was running. The Passback button appears in the bottom right corner. Pressing Alt+Shift+R does the same as a click on it.Restart your agent in the project and approve the
passbackserver when it asks. Then start a session: type/passbackin Claude Code or Cursor,$passbackin Codex. The agent waits for your first comment.
.passback/inbox in your project.What init changes in a Next app
Init installs no dependencies. Bare npx passback init only plans: on a fresh project it creates .passback/inbox and .passback/outbox, updates .gitignore, and prints the remaining proposed writes as "not written (add --apply to write it)". A plugin configuration that is already wired can still be updated without --apply, so this is not a side-effect-free preview. --apply writes the full list here.
With --apply, for each Next app it finds, it writes:
- The route
app/api/passback/[[...path]]/route.ts(undersrc/appif your app keeps its code there). It serves the widget and takes the comments, and only answers undernext dev. - Your Next config: its export is wrapped in
withPassback(…). Without a config, init createsnext.config.mjs. A config it does not recognise is left as it is, and init prints the line to add. - Your root layout (
app/layout.tsx,.jsxor.js):<Passback />inside<head>, and its import. A layout it does not recognise is left as it is, and init prints what to add by hand:import { Passback } from '@passback/next', and<Passback />inside<head>of the layout that renders<html>.
It also writes what every project gets: a Passback block in AGENTS.md, and .passback/inbox and .passback/outbox with a .passback/ line in .gitignore. The files of an agent are written only when that agent is selected: .mcp.json and .claude/commands/passback.md for Claude Code, .codex/config.toml and .agents/skills/passback/SKILL.md for Codex, .cursor/mcp.json and .cursor/skills/passback/SKILL.md for Cursor.
By default, init sets up only the agents detected in your repository; if none are detected, it sets up Claude Code. Explicit agent names override detection: npx passback init --agents claude --apply (or codex, cursor, several joined by commas) sets up only those. npx passback init --agents all --apply restores the previous behavior of setting up Claude Code, Codex and Cursor regardless of existing folders.
If .mcp.json, .cursor/mcp.json or the Passback block in AGENTS.md is broken, it leaves that file as it is and prints what to fix. Applying a plan does not guarantee that an unsupported config or layout can be rewritten. Running init again is safe: it leaves what is already set up as it is.
Send comments to your tracker from a deployed build
The same widget can run in a deployed build. It then files each comment, with the drawing and a screenshot, in the tracker connected to your site (page Connect a tracker). Your layout carries the line:
<Passback mode={process.env.NODE_ENV === 'production' ? 'relay' : 'agent'} siteKey="…" />You can write it yourself, or run npx passback init --site --apply. The flag takes an optional site key (--site <site key>) and needs a login first (npx passback login). It uses the site of your account that has the key, or else the site named after your project's folder, and creates that site if it does not exist. It then writes siteKey and, if the tag has no mode yet, the mode line into the one <Passback /> tag of your root layout. Init omits endpoint when it equals https://api.passback.dev and writes it only for another relay. If the layout has no single <Passback /> tag, init leaves it as it is and prints what to add by hand.
siteKey comes from the Install section of your site in the dashboard. screenshots={false} turns the picture off.
| Option | What it does |
|---|---|
siteKey |
The site key from the Install section of your site in the dashboard. Needed for a deployed build. |
endpoint |
Optional; defaults to https://api.passback.dev. Rarely needed: set it only to use another relay. |
The route file you already have serves the widget in the deployed build. The comments do not go to the route: they go to the relay at https://api.passback.dev, or at endpoint when you set it.
Add the address the app is served from to the site's Allowed origins in the Install section (page Install with a script tag, section Allowed origins). A comment from any other address is refused.
siteKey is set. Without it nothing from Passback is rendered. The file and line of a component, the local inbox and the agent are for development only (next dev).