Getting started
Install with Vite + React
Passback adds a small button to your site 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 project that runs on Vite 7 or 8.
- Node 22 or later.
- One of Claude Code, Codex or Cursor, opened in the project.
Install
In your project folder, add the two packages:
npm i -D passback @passback/vite-pluginSet the project up:
npx passback init --applyInit adds the plugin to your Vite config. If it cannot edit the config safely, it leaves the file as it is and prints these lines to add by hand:
// vite.config.ts import passback from '@passback/vite-plugin'; export default { plugins: [passback()], };Start your dev server as usual, 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
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 it writes:
- Your Vite config: the import and
passback()in itspluginslist. A config it does not recognise, for example one exported as a function, is left as it is, and init prints the lines to add. AGENTS.md: a short Passback block that tells the agent how to read comments..passback/inboxand.passback/outbox: the two folders that comments and progress travel through, and a.passback/line in.gitignore.
Those are the shared files. The files of an agent are written only when that agent is selected:
- Claude Code:
.mcp.json, the entry that lets the agent start the Passback server (npx passback dev), and.claude/commands/passback.md. - Codex:
.codex/config.tomland.agents/skills/passback/SKILL.md. - Cursor:
.cursor/mcp.jsonand.cursor/skills/passback/SKILL.md.
You never run npx passback dev yourself.
By default, init sets up only the agents detected in your repository; if none are detected, it sets up Claude Code. Init looks for these signals in the project root:
| Signal | Agent |
|---|---|
.claude/ |
Claude Code |
.codex/ or .agents/ |
Codex |
.cursor/ |
Cursor |
.mcp.json (file) |
Claude Code |
AGENTS.md (file) |
Codex |
The folders count first. The two files count only when none of the four folders exists. Init prints one line per selected agent with the reason, for example agents: claude (detected: .claude/), agents: claude (fallback: no agent markers found) or agents: codex (--agents).
Explicit agent names override detection. Comma-separated names select several agents:
npx passback init --agents claude,cursor --applyTo restore the previous behavior of setting up Claude Code, Codex and Cursor regardless of existing folders, run:
npx passback init --agents all --applyIf .mcp.json, .cursor/mcp.json or the Passback block in AGENTS.md is broken, init leaves that file as it is and prints what to fix. Applying a plan does not guarantee that an unsupported config can be rewritten. Running init again is safe: it leaves what is already set up as it is.
Plugin options
| Option | Value | What it does |
|---|---|---|
stagingModes |
a list of build modes, for example ['staging'] |
A build in one of these modes (vite build --mode staging) includes the widget. Default: none. |
buildId |
a text | Ties comments to one build. Default: the short hash of the current git commit. |
root |
a folder, relative to the Vite root | Where the .passback folder lives, for a project inside a larger repository. Default: the Vite root. |
mode |
'agent', 'relay' or 'off' |
Where comments go: 'agent' to your coding agent, 'relay' to your tracker, 'off' shows no widget. Default: 'agent' in development; in a build, 'relay' when siteKey is set and 'agent' otherwise. |
siteKey |
a text | The site key from the Install section of your site in the dashboard. Needed for 'relay'. |
endpoint |
an address | Optional; defaults to https://api.passback.dev. Rarely needed: set it only to use another relay. |
screenshots |
true or false |
Lets a comment carry a picture of the page. Default: on in 'relay' mode. Pictures need the Premium plan. |
The look of the button and the box has its own settings: see Widget settings.
A project with several build modes
A project with separate web and Capacitor builds lists each staging mode, so the widget appears only in staging builds:
passback({
mode: 'relay',
stagingModes: ['web-dev', 'app-dev'],
siteKey: '…',
});siteKey is a placeholder: replace it with the site key from the Install section of your site in the dashboard. A hosted setup needs only the site key; comments go to https://api.passback.dev.
The first build emits the widget and the second emits none:
vite build --mode web-dev
vite build --mode web-prodThen check the production output:
grep -rl passback distAfter the production build, this check prints nothing.
Where the widget appears
| Where | Widget |
|---|---|
| The dev server | Shown. |
| A build in a listed staging mode | Included. |
| Any other build | Nothing from Passback; a relay build warns once, naming stagingModes. |
| A production build | Nothing, while its build mode is not in stagingModes. |
Mode 'off' shows no widget anywhere. A siteKey with 'relay' cannot bypass stagingModes: a build emits the widget only when its Vite mode is listed. Production stays excluded only while its build mode, for example web-prod or app-prod, is absent from the list, so keep production modes out of stagingModes.
Hosted setup
npx passback init --site --apply writes siteKey and stagingModes: ['staging'] into your Vite config. It writes endpoint only for a relay other than https://api.passback.dev. If the config already has stagingModes, init keeps your list as it is, including an empty one, and adds nothing. Init then prints the staging build command for each Vite config, with its directory:
vite build --mode stagingInit does not run it. For the build to include the widget, staging must be listed in stagingModes; with your own list, add staging or build with a mode already listed.