vercel-sandbox
Run agent-browser + Chrome inside Vercel Sandbox microVMs for browser automation from any Vercel-deployed app. Use when the user needs browser automation in a Vercel app (Next.js, SvelteKit, Nuxt, Remix, Astro, etc.), wants to run headless Chrome without binary size limits, needs persistent browser sessions across commands, or wants ephemeral isolated browser environments. Triggers include "Vercel Sandbox browser", "microVM Chrome", "agent-browser in sandbox", "browser automation on Vercel", or any task requiring Chrome in a Vercel Sandbox.
Best use case
vercel-sandbox is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Run agent-browser + Chrome inside Vercel Sandbox microVMs for browser automation from any Vercel-deployed app. Use when the user needs browser automation in a Vercel app (Next.js, SvelteKit, Nuxt, Remix, Astro, etc.), wants to run headless Chrome without binary size limits, needs persistent browser sessions across commands, or wants ephemeral isolated browser environments. Triggers include "Vercel Sandbox browser", "microVM Chrome", "agent-browser in sandbox", "browser automation on Vercel", or any task requiring Chrome in a Vercel Sandbox.
Teams using vercel-sandbox should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/vercel-sandbox/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How vercel-sandbox Compares
| Feature / Agent | vercel-sandbox | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Run agent-browser + Chrome inside Vercel Sandbox microVMs for browser automation from any Vercel-deployed app. Use when the user needs browser automation in a Vercel app (Next.js, SvelteKit, Nuxt, Remix, Astro, etc.), wants to run headless Chrome without binary size limits, needs persistent browser sessions across commands, or wants ephemeral isolated browser environments. Triggers include "Vercel Sandbox browser", "microVM Chrome", "agent-browser in sandbox", "browser automation on Vercel", or any task requiring Chrome in a Vercel Sandbox.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Browser Automation with Vercel Sandbox
Run agent-browser + headless Chrome inside ephemeral Vercel Sandbox microVMs. A Linux VM spins up on demand, executes browser commands, and shuts down. Works with any Vercel-deployed framework (Next.js, SvelteKit, Nuxt, Remix, Astro, etc.).
## Dependencies
```bash
pnpm add @vercel/sandbox
```
The sandbox VM needs system dependencies for Chromium plus agent-browser itself. Use sandbox snapshots (below) to pre-install everything for sub-second startup.
## Core Pattern
```ts
import { Sandbox } from "@vercel/sandbox";
// System libraries required by Chromium on the sandbox VM (Amazon Linux / dnf)
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 snapshotId = process.env.AGENT_BROWSER_SNAPSHOT_ID;
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();
}
}
```
## Screenshot
The `screenshot --json` command saves to a file and returns the path. Read the file back as base64:
```ts
export async function screenshotUrl(url: string) {
return withBrowser(async (sandbox) => {
await sandbox.runCommand("agent-browser", ["open", url]);
const titleResult = await sandbox.runCommand("agent-browser", [
"get", "title", "--json",
]);
const title = JSON.parse(await titleResult.stdout())?.data?.title || 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 { title, screenshot };
});
}
```
## Accessibility Snapshot
```ts
export async function snapshotUrl(url: string) {
return withBrowser(async (sandbox) => {
await sandbox.runCommand("agent-browser", ["open", url]);
const titleResult = await sandbox.runCommand("agent-browser", [
"get", "title", "--json",
]);
const title = JSON.parse(await titleResult.stdout())?.data?.title || url;
const snapResult = await sandbox.runCommand("agent-browser", [
"snapshot", "-i", "-c",
]);
const snapshot = await snapResult.stdout();
await sandbox.runCommand("agent-browser", ["close"]);
return { title, snapshot };
});
}
```
## Multi-Step Workflows
The sandbox persists between commands, so you can run full automation sequences:
```ts
export async function fillAndSubmitForm(url: string, data: Record<string, string>) {
return withBrowser(async (sandbox) => {
await sandbox.runCommand("agent-browser", ["open", url]);
const snapResult = await sandbox.runCommand("agent-browser", [
"snapshot", "-i",
]);
const snapshot = await snapResult.stdout();
// Parse snapshot to find element refs...
for (const [ref, value] of Object.entries(data)) {
await sandbox.runCommand("agent-browser", ["fill", ref, value]);
}
await sandbox.runCommand("agent-browser", ["click", "@e5"]);
await sandbox.runCommand("agent-browser", ["wait", "--load", "networkidle"]);
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 { screenshot };
});
}
```
## Sandbox Snapshots (Fast Startup)
A **sandbox snapshot** is a saved VM image of a Vercel Sandbox with system dependencies + agent-browser + Chromium already installed. Think of it like a Docker image -- instead of installing dependencies from scratch every time, the sandbox boots from the pre-built image.
This is unrelated to agent-browser's *accessibility snapshot* feature (`agent-browser snapshot`), which dumps a page's accessibility tree. A sandbox snapshot is a Vercel infrastructure concept for fast VM startup.
Without a sandbox snapshot, each run installs system deps + agent-browser + Chromium (~30s). With one, startup is sub-second.
### Creating a sandbox snapshot
The snapshot must include system dependencies (via `dnf`), agent-browser, and Chromium:
```ts
import { Sandbox } from "@vercel/sandbox";
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",
];
async function createSnapshot(): Promise<string> {
const sandbox = await Sandbox.create({
runtime: "node24",
timeout: 300_000,
});
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"]);
const snapshot = await sandbox.snapshot();
return snapshot.snapshotId;
}
```
Run this once, then set the environment variable:
```bash
AGENT_BROWSER_SNAPSHOT_ID=snap_xxxxxxxxxxxx
```
A helper script is available in the demo app:
```bash
npx tsx examples/environments/scripts/create-snapshot.ts
```
Recommended for any production deployment using the Sandbox pattern.
## Authentication
On Vercel deployments, the Sandbox SDK authenticates automatically via OIDC. For local development or explicit control, set:
```bash
VERCEL_TOKEN=<personal-access-token>
VERCEL_TEAM_ID=<team-id>
VERCEL_PROJECT_ID=<project-id>
```
These are spread into `Sandbox.create()` calls. When absent, the SDK falls back to `VERCEL_OIDC_TOKEN` (automatic on Vercel).
## Scheduled Workflows (Cron)
Combine with Vercel Cron Jobs for recurring browser tasks:
```ts
// app/api/cron/route.ts (or equivalent in your framework)
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 });
}
```
```json
// vercel.json
{ "crons": [{ "path": "/api/cron", "schedule": "0 9 * * *" }] }
```
## Environment Variables
| Variable | Required | Description |
|---|---|---|
| `AGENT_BROWSER_SNAPSHOT_ID` | No (but recommended) | Pre-built sandbox snapshot ID for sub-second startup (see above) |
| `VERCEL_TOKEN` | No | Vercel personal access token (for local dev; OIDC is automatic on Vercel) |
| `VERCEL_TEAM_ID` | No | Vercel team ID (for local dev) |
| `VERCEL_PROJECT_ID` | No | Vercel project ID (for local dev) |
## Framework Examples
The pattern works identically across frameworks. The only difference is where you put the server-side code:
| Framework | Server code location |
|---|---|
| Next.js | Server actions, API routes, route handlers |
| SvelteKit | `+page.server.ts`, `+server.ts` |
| Nuxt | `server/api/`, `server/routes/` |
| Remix | `loader`, `action` functions |
| Astro | `.astro` frontmatter, API routes |
## Example
See `examples/environments/` in the agent-browser repo for a working app with the Vercel Sandbox pattern, including a sandbox snapshot creation script, streaming progress UI, and rate limiting.Related Skills
what-not-to-do-as-product-manager
Anti-patterns and mistakes to avoid as a product manager. Use when evaluating leadership behaviors, improving team dynamics, reflecting on management practices, or onboarding new product managers.
visual-cues-cta-psychology
Design effective CTAs using visual attention and gaze psychology principles. Use when designing landing pages, button hierarchies, conversion elements, or optimizing user attention flow through interfaces.
value-realization
Analyze if end users discover clear value. Use when evaluating product concepts, analyzing adoption, or uncertain about direction.
user-story-fundamentals
Capture requirements from user perspective with structured user stories. Use when writing backlog items, defining acceptance criteria, prioritizing features, or communicating requirements between product and development.
typescript-satisfies-operator
Guides proper usage of TypeScript's satisfies operator vs type annotations. Use this skill when deciding between type annotations (colon) and satisfies, validating object shapes while preserving literal types, or troubleshooting type inference issues.
typescript-interface-vs-type
Guides when to use interface vs type in TypeScript. Use this skill when defining object types, extending types, or choosing between interface and type aliases.
typescript-best-practices
Guides TypeScript best practices for type safety, code organization, and maintainability. Use this skill when configuring TypeScript projects, deciding on typing strategies, writing async code, or reviewing TypeScript code quality.
typescript-advanced-types
Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript projects.
trust-psychology
Build trust signals that reduce perceived risk and enable user action. Use when designing landing pages, checkout flows, onboarding experiences, or any conversion point where user hesitation is a barrier.
theme-epic-story
Structure product work hierarchically using themes, epics, and stories. Use when organizing backlogs, planning releases, communicating with stakeholders, or breaking down large initiatives into manageable work.
tailwind-v4-configuration
Configure Tailwind CSS v4 with CSS-first approach. Use when installing, migrating from v3, setting up build tools (Vite/PostCSS/CLI), customizing themes with @theme, or configuring plugins.
status-quo-bias
Understand and design for users' preference for current state over change. Use when planning migrations, introducing new features, designing defaults, or overcoming resistance to product adoption.