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

pnpm add @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.

"use server";
import { Sandbox } from "@vercel/sandbox";

const snapshotId = process.env.AGENT_BROWSER_SNAPSHOT_ID;

const CHROMIUM_SYSTEM_DEPS = [
  "nss", "nspr", "libxkbcommon", "atk", "at-spi2-atk", "at-spi2-core",
  "libXcomposite", "libXdamage", "libXrandr", "libXfixes", "libXcursor",
  "libXi", "libXtst", "libXScrnSaver", "libXext", "mesa-libgbm", "libdrm",
  "mesa-libGL", "mesa-libEGL", "cups-libs", "alsa-lib", "pango", "cairo",
  "gtk3", "dbus-libs",
];

function getSandboxCredentials() {
  if (
    process.env.VERCEL_TOKEN &&
    process.env.VERCEL_TEAM_ID &&
    process.env.VERCEL_PROJECT_ID
  ) {
    return {
      token: process.env.VERCEL_TOKEN,
      teamId: process.env.VERCEL_TEAM_ID,
      projectId: process.env.VERCEL_PROJECT_ID,
    };
  }
  return {};
}

async function withBrowser​​T​​(
  fn: (sandbox: InstanceType<typeof Sandbox>) => Promise​​T​​,
): Promise​​T​​ {
  const credentials = getSandboxCredentials();

  const sandbox = snapshotId
    ? await Sandbox.create({
        ...credentials,
        source: { type: "snapshot", snapshotId },
        timeout: 120_000,
      })
    : await Sandbox.create({ ...credentials, runtime: "node24", timeout: 120_000 });

  if (!snapshotId) {
    await sandbox.runCommand("sh", [
      "-c",
      `sudo dnf clean all 2>&1 && sudo dnf install -y --skip-broken ${CHROMIUM_SYSTEM_DEPS.join(" ")} 2>&1 && sudo ldconfig 2>&1`,
    ]);
    await sandbox.runCommand("npm", ["install", "-g", "agent-browser"]);
    await sandbox.runCommand("npx", ["agent-browser", "install"]);
  }

  try {
    return await fn(sandbox);
  } finally {
    await sandbox.stop();
  }
}

export async function screenshotUrl(url: string) {
  return withBrowser(async (sandbox) => {
    await sandbox.runCommand("agent-browser", ["open", url]);

    const ssResult = await sandbox.runCommand("agent-browser", [
      "screenshot", "--json",
    ]);
    const ssPath = JSON.parse(await ssResult.stdout())?.data?.path;
    const b64Result = await sandbox.runCommand("base64", ["-w", "0", ssPath]);
    const screenshot = (await b64Result.stdout()).trim();

    await sandbox.runCommand("agent-browser", ["close"]);
    return { ok: true, screenshot };
  });
}

export async function snapshotUrl(url: string) {
  return withBrowser(async (sandbox) => {
    await sandbox.runCommand("agent-browser", ["open", url]);

    const result = await sandbox.runCommand("agent-browser", [
      "snapshot", "-i", "-c",
    ]);
    const snapshot = await result.stdout();

    await sandbox.runCommand("agent-browser", ["close"]);
    return { ok: true, snapshot };
  });
}

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 -- like 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:

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:

VariableDescription
VERCEL_TOKENVercel personal access token
VERCEL_TEAM_IDVercel team ID
VERCEL_PROJECT_IDVercel project ID

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:

// app/api/cron/monitor/route.ts
export async function GET() {
  const result = await withBrowser(async (sandbox) => {
    await sandbox.runCommand("agent-browser", [
      "open", "https://example.com/pricing",
    ]);
    const snap = await sandbox.runCommand("agent-browser", [
      "snapshot", "-i", "-c",
    ]);
    await sandbox.runCommand("agent-browser", ["close"]);
    return await snap.stdout();
  });

  // Process results, send alerts, store data...
  return Response.json({ ok: true, snapshot: result });
}
// vercel.json
{
  "crons": [
    { "path": "/api/cron/monitor", "schedule": "0 9 * * *" }
  ]
}

Environment variables

VariableDescription
AGENT_BROWSER_SNAPSHOT_IDSandbox snapshot ID for sub-second startup (see above)
VERCEL_TOKENVercel personal access token (for local dev; OIDC is automatic on Vercel)
VERCEL_TEAM_IDVercel team ID (for local dev)
VERCEL_PROJECT_IDVercel project ID (for local dev)

Demo app

A working demo with streaming progress UI, rate limiting, and a deploy-to-Vercel button is at examples/environments/.