next.js example (#694)

* next.js guide

* better

* shadcn

* fixes

* fix: correct screenshot test assertion to check path instead of base64

The daemon returns { path: savePath } for screenshot commands, not base64.

* fix: cross-platform Chrome detection and gitignore hardening

- Replace hardcoded macOS Chrome path with findLocalChrome() that
  searches common paths on macOS, Linux, and WSL, with a clear error
  message when no Chrome is found.
- Add .env and .env*.local to .gitignore to prevent accidental
  secret commits.

* fix: correct Vercel deploy button repo URL to vercel-labs/agent-browser

* clean up

* demo

* next page

---------

Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
This commit is contained in:
Chris Tate
2026-03-09 15:24:44 -05:00
committed by GitHub
co-authored by ctate
parent 3649787268
commit cc3c70dc86
35 changed files with 6764 additions and 2 deletions
+207
View File
@@ -0,0 +1,207 @@
import { pageMetadata } from "@/lib/page-metadata"
export const metadata = pageMetadata("next")
# Next.js + Vercel
Two patterns for running agent-browser from Next.js on Vercel.
## Pattern 1: Serverless Function
Run `@sparticuz/chromium` + `puppeteer-core` directly inside a Vercel
serverless function. No external server needed.
### Setup
```bash
pnpm add @sparticuz/chromium puppeteer-core
```
### Browser launcher
```ts
import puppeteer from "puppeteer-core";
import chromium from "@sparticuz/chromium";
import fs from "node:fs";
const CHROME_PATHS = [
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
"/usr/bin/google-chrome",
"/usr/bin/chromium",
"/usr/bin/chromium-browser",
];
function findLocalChrome(): string {
for (const p of CHROME_PATHS) {
if (fs.existsSync(p)) return p;
}
throw new Error("Chrome not found. Set CHROMIUM_PATH.");
}
async function launchBrowser() {
const isLambda =
!!process.env.VERCEL || !!process.env.AWS_LAMBDA_FUNCTION_NAME;
return puppeteer.launch({
args: isLambda ? chromium.args : ["--no-sandbox"],
executablePath: isLambda
? await chromium.executablePath()
: process.env.CHROMIUM_PATH || findLocalChrome(),
headless: true,
defaultViewport: { width: 1280, height: 720 },
});
}
```
On Vercel, `@sparticuz/chromium` provides the binary automatically.
Locally, the launcher finds your system Chrome or uses `CHROMIUM_PATH`.
### Server action
```ts
"use server";
export async function screenshotUrl(url: string) {
const browser = await launchBrowser();
try {
const page = await browser.newPage();
await page.goto(url, { waitUntil: "networkidle2", timeout: 30_000 });
const title = await page.title();
const screenshot = await page.screenshot({ encoding: "base64" });
return { ok: true, title, screenshot };
} finally {
await browser.close();
}
}
```
### Next.js config
```ts
// next.config.ts
const nextConfig = {
serverExternalPackages: ["@sparticuz/chromium"],
};
export default nextConfig;
```
## Pattern 2: Vercel Sandbox
A Linux microVM spins up on demand, runs agent-browser + Chrome, and
shuts down. No binary size limits. Best for multi-step workflows or
when you need full Chrome capabilities.
### Server action
```ts
"use server";
import { Sandbox } from "@vercel/sandbox";
export async function screenshotUrl(url: string) {
const snapshotId = process.env.AGENT_BROWSER_SNAPSHOT_ID;
const sandbox = snapshotId
? await Sandbox.create({
source: { type: "snapshot", snapshotId },
timeout: 120_000,
})
: await Sandbox.create({ runtime: "node24", timeout: 120_000 });
try {
if (!snapshotId) {
await sandbox.runCommand("npm", ["install", "-g", "agent-browser"]);
await sandbox.runCommand("npx", ["agent-browser", "install"]);
}
await sandbox.runCommand("agent-browser", ["open", url]);
const result = await sandbox.runCommand("agent-browser", [
"screenshot", "--json",
]);
const stdout = await result.stdout();
const data = JSON.parse(stdout);
await sandbox.runCommand("agent-browser", ["close"]);
return { ok: true, screenshot: data.data.base64 };
} finally {
await sandbox.stop();
}
}
```
### Optimize with snapshots
Installing agent-browser + Chromium takes ~30 seconds. Create a
snapshot to make sandbox creation sub-second:
```bash
npx tsx scripts/create-snapshot.ts
# Output: AGENT_BROWSER_SNAPSHOT_ID=snap_xxxxxxxxxxxx
```
Add the snapshot ID to your Vercel environment variables or `.env.local`.
## Scheduled workflows (cron)
For recurring tasks like daily monitoring, use Vercel Cron Jobs with
either pattern:
```ts
// app/api/cron/monitor/route.ts
export async function GET() {
const browser = await launchBrowser();
try {
const page = await browser.newPage();
await page.goto("https://example.com/pricing", {
waitUntil: "networkidle2",
});
const snapshot = await page.accessibility.snapshot();
// Process results, send alerts, store in database...
return Response.json({ ok: true, snapshot });
} finally {
await browser.close();
}
}
```
```json
// vercel.json
{
"crons": [
{ "path": "/api/cron/monitor", "schedule": "0 9 * * *" }
]
}
```
## When to use which pattern
<table>
<thead>
<tr><th></th><th>Serverless Function</th><th>Vercel Sandbox</th></tr>
</thead>
<tbody>
<tr><td>Infrastructure</td><td>None (runs in-function)</td><td>None (Vercel manages VMs)</td></tr>
<tr><td>Startup time</td><td>~2-3 seconds</td><td>Sub-second with snapshot</td></tr>
<tr><td>Binary size limit</td><td>50MB compressed</td><td>None</td></tr>
<tr><td>Multi-step workflows</td><td>Single request only</td><td>Yes (persistent session)</td></tr>
<tr><td>Task duration</td><td>Up to 300s (Pro)</td><td>Up to ~2 minutes</td></tr>
<tr><td>Best for</td><td>Fast single-request screenshots</td><td>Complex workflows, full Chrome</td></tr>
</tbody>
</table>
## Environment variables
<table>
<thead>
<tr><th>Variable</th><th>Pattern</th><th>Description</th></tr>
</thead>
<tbody>
<tr><td><code>CHROMIUM_PATH</code></td><td>Serverless</td><td>Path to local Chrome/Chromium (auto-detected on Vercel)</td></tr>
<tr><td><code>AGENT_BROWSER_SNAPSHOT_ID</code></td><td>Sandbox</td><td>Pre-built snapshot for fast startup</td></tr>
</tbody>
</table>
## Demo app
A working demo with both patterns, a UI, and a deploy-to-Vercel button is at
[`examples/demo/`](https://github.com/agent-browser/agent-browser/tree/main/examples/demo).