---
title: "Next.js + Vercel"
description: "Browser automation CLI for AI agents"
canonical_url: "https://agent-browser.dev/next"
---

# Next.js + Vercel

Run agent-browser from a Next.js app on Vercel using Vercel Sandbox. A Linux microVM spins up on demand, runs agent-browser + Chrome, and shuts down. No binary size limits, no Chromium bundling complexity.

## Setup

```bash
pnpm add @agent-browser/sandbox @vercel/sandbox
```

## Server action

The Vercel Sandbox runs Amazon Linux. Chromium requires system libraries that are not installed by default, so fresh sandboxes need a `dnf install` step before agent-browser can launch Chrome. Use a sandbox snapshot (below) to skip this entirely in production. The `@agent-browser/sandbox` helper installs those system dependencies by default and handles command execution.

```ts
"use server";
import { runAgentBrowserCommand, withAgentBrowserSandbox } from "@agent-browser/sandbox/vercel";

export async function screenshotUrl(url: string) {
  return withAgentBrowserSandbox(async (sandbox) => {
    await runAgentBrowserCommand(sandbox, ["open", url]);

    const ssResult = await runAgentBrowserCommand<{ data?: { path?: string } }>(sandbox, [
      "screenshot",
    ]);
    const ssPath = ssResult.json?.data?.path;
    if (!ssPath) throw new Error("Screenshot did not return a file path.");

    const b64Result = await sandbox.runCommand("base64", ["-w", "0", ssPath]);
    const screenshot = (await b64Result.stdout()).trim();
    await runAgentBrowserCommand(sandbox, ["close"], { json: false });
    return { ok: true, screenshot };
  });
}

export async function snapshotUrl(url: string) {
  return withAgentBrowserSandbox(async (sandbox) => {
    await runAgentBrowserCommand(sandbox, ["open", url]);

    const result = await runAgentBrowserCommand(sandbox, ["snapshot", "-i", "-c"], {
      json: false,
    });

    await runAgentBrowserCommand(sandbox, ["close"], { json: false });
    return { ok: true, snapshot: result.stdout };
  });
}
```

## MCP server over stdin

When an agent makes many MCP tool calls in the same Vercel Sandbox, use `startAgentBrowserMcp` from `@agent-browser/sandbox/vercel`. It starts one `agent-browser mcp` process and sends every request over that command's stdin, so tool calls reuse the process instead of starting a CLI command each time as `runAgentBrowserCommand` does:

```typescript
import { startAgentBrowserMcp, withAgentBrowserSandbox } from "@agent-browser/sandbox/vercel";

const snapshot = await withAgentBrowserSandbox(async (sandbox) => {
  const mcp = await startAgentBrowserMcp(sandbox, { timeout: 60_000 });
  try {
    await mcp.callTool("agent_browser_open", { url: "https://example.com" });
    return await mcp.callTool("agent_browser_snapshot", { interactive: true });
  } finally {
    await mcp.close();
  }
});
```

`startAgentBrowserMcp` requires `@vercel/sandbox` 3.7 or later. `timeout` is in milliseconds and applies to each request and to `close()`; without it, requests wait indefinitely. In another runtime, pass a process with `stdin` and `stdout` streams to `connectAgentBrowserMcp` from `@agent-browser/sandbox`.

## Sandbox snapshots

Without optimization, each Sandbox run installs system dependencies + agent-browser + Chromium from scratch (~30 seconds). A **sandbox snapshot** is a saved VM image with everything pre-installed, similar to a Docker image for Vercel Sandbox. When `AGENT_BROWSER_SNAPSHOT_ID` is set, the sandbox boots from that image instead of installing, bringing startup down to sub-second.

This is different from an agent-browser *accessibility snapshot* (which dumps a page's accessibility tree). A sandbox snapshot is a Vercel infrastructure concept.

Create a sandbox snapshot by running the helper script once:

```bash
npx tsx scripts/create-snapshot.ts
```

The script spins up a fresh sandbox, installs system dependencies + agent-browser + Chromium, saves the VM state, and prints the snapshot ID:

```
AGENT_BROWSER_SNAPSHOT_ID=snap_xxxxxxxxxxxx
```

Add this to your Vercel project environment variables (or `.env.local` for local development). Recommended for any production deployment.

## Authentication

On Vercel deployments, the Sandbox SDK authenticates automatically via OIDC. For local development, provide explicit credentials:

<table>
  <thead>
    <tr><th>Variable</th><th>Description</th></tr>
  </thead>
  <tbody>
    <tr><td><code>VERCEL_TOKEN</code></td><td>Vercel personal access token</td></tr>
    <tr><td><code>VERCEL_TEAM_ID</code></td><td>Vercel team ID</td></tr>
    <tr><td><code>VERCEL_PROJECT_ID</code></td><td>Vercel project ID</td></tr>
  </tbody>
</table>

When all three are set, they are passed to `Sandbox.create()`. When absent, the SDK falls back to `VERCEL_OIDC_TOKEN` (automatic on Vercel).

## Scheduled workflows (cron)

For recurring tasks like daily monitoring, use Vercel Cron Jobs:

```ts
// app/api/cron/monitor/route.ts
import { runAgentBrowserCommand, withAgentBrowserSandbox } from "@agent-browser/sandbox/vercel";

export async function GET() {
  const result = await withAgentBrowserSandbox(async (sandbox) => {
    await runAgentBrowserCommand(sandbox, [
      "open", "https://example.com/pricing",
    ]);
    const snap = await runAgentBrowserCommand(sandbox, [
      "snapshot", "-i", "-c",
    ], { json: false });
    await runAgentBrowserCommand(sandbox, ["close"], { json: false });
    return snap.stdout;
  });

  // Process results, send alerts, store data...
  return Response.json({ ok: true, snapshot: result });
}
```

```json
// vercel.json
{
  "crons": [
    { "path": "/api/cron/monitor", "schedule": "0 9 * * *" }
  ]
}
```

## Environment variables

<table>
  <thead>
    <tr><th>Variable</th><th>Description</th></tr>
  </thead>
  <tbody>
    <tr><td><code>AGENT_BROWSER_SNAPSHOT_ID</code></td><td>Sandbox snapshot ID for sub-second startup (see above)</td></tr>
    <tr><td><code>VERCEL_TOKEN</code></td><td>Vercel personal access token (for local dev; OIDC is automatic on Vercel)</td></tr>
    <tr><td><code>VERCEL_TEAM_ID</code></td><td>Vercel team ID (for local dev)</td></tr>
    <tr><td><code>VERCEL_PROJECT_ID</code></td><td>Vercel project ID (for local dev)</td></tr>
  </tbody>
</table>

## Demo app

A working demo with streaming progress UI, rate limiting, and a deploy-to-Vercel button is at [`examples/environments/`](https://github.com/vercel-labs/agent-browser/tree/main/examples/environments).
