# Give Your Box a Browser

> **Source:** https://upstash.com/blog/box-browser
> **Date:** 2026-08-18
> **Author(s):** Ali Tarık Şahin
> **Reading time:** 6 min read
> **Tags:** box, ai, browser
> **Format:** text/markdown — machine-readable content for agents and LLMs

Every Upstash Box can now come with its own headless browser. Your agent can scrape a page and act on what it finds, in one place, because the browser lives inside a real computer.

---

Every [Upstash Box](/blog/upstash-box) can now come with its own browser. Create a box with `browser: true` and you get a managed, headless Chromium you control through the SDK. Open tabs, read pages, take screenshots, extract structured data, run AI agents on the live DOM, or connect Playwright straight over CDP.

The interesting part is not the browser. It's where the browser lives.

  - Add `browser: true` at create time and every box gets a headless Chromium.
  - Read pages, screenshot, and extract schema-validated data off the live DOM.
  - Drive it two ways: raw Playwright/CDP, or single natural-language actions on the live page.
  - It lives inside a real computer, so you can scrape a page and then process, write, and commit the result without leaving the box.

---

## A browser wants a computer around it

Cloud browsers are not new. You can rent a headless Chromium from a handful of services, point Playwright at it, and scrape the web. That part is solved.

The problem shows up right after. Your agent loads a page, pulls the data it needs, and then what? The data has to go somewhere. It gets cleaned, joined with something else, written to a file, committed to a repo. A browser on its own can't do any of that. So you wire the browser to a second environment that can, and now you are running two things that have to find each other, share credentials, and pass data back and forth.

A box already is that second environment. It has a full shell, a persistent filesystem, git, and a coding agent, all in one isolated container. Putting the browser inside it means the page you just scraped and the code that acts on it are on the same machine. Nothing to wire up.

```typescript
import { Box } from "@upstash/box"

const box = await Box.create({
  runtime: "node",
  browser: true,
})

// Boots Chromium on first use
const tab = await box.browser.tab.create("https://news.ycombinator.com")

// Capture the page as a PNG, no display needed
const png = await tab.screenshot()
```

Everything is headless. There is no desktop, no VNC, nothing to install. Chromium is provisioned with the box and boots on the first tab.

---

## What you get

`box.browser` manages the browser itself. Page-level operations live on a **Tab** handle, addressed by its CDP target id so it stays valid across navigations and even across processes.

**Read any page** - `content()` returns the title, visible text, and links from the real DOM, including JavaScript-rendered content.

**Screenshots** - `screenshot()` captures the tab as a PNG, full-page or viewport, with no display needed.

**Schema-validated extraction** - `extract()` hands the page to a DOM-aware agent and returns data parsed against your Zod or pydantic schema, so a successful call always gives you the shape you asked for.

**Tabs that survive** - open as many as you want, list them, and re-attach to one from a different process by its id.

**Live view** - a tokenized, view-only stream of any tab you can embed in an iframe.

**Recordings** - capture a session to replayable video with chapter markers for each tab switch and agent run.

**Bring your own tools** - `cdpUrl()` returns an authenticated endpoint you can drive with Playwright, Puppeteer, or Stagehand.

---

## Two ways to drive it

The same browser answers to two levels of control, and you can mix them on the same tab.

**Raw and deterministic.** Point Playwright at the box over CDP and run your existing scripts. No LLM in the loop.

```typescript
import { chromium } from "playwright-core"

const cdpUrl = await box.browser.cdpUrl()
const browser = await chromium.connectOverCDP(cdpUrl)
```

**One action at a time.** `observe()` finds the actionable elements on a page and `act()` resolves and executes a single instruction against the live DOM. Good for flows where your code decides each step. A resolved action replays with no LLM, so you can cache the expensive step and reuse it.

```typescript
await tab.act("click the sign-in button")
```

Because both drive the same tabs, you can script the brittle parts, like login and pagination, with Playwright, then hand the tab to `act` for the fuzzy parts.

---

## Scrape, then act on it

Here is the part a standalone browser can't do. This box scrapes a page, extracts structured data, writes it to disk, and commits it. Same container, start to finish.

```typescript
import { Box } from "@upstash/box"
import { z } from "zod"

const box = await Box.create({ runtime: "node", browser: true })

// 1. Scrape: open the page in the box's browser
const tab = await box.browser.tab.create("https://news.ycombinator.com")

// 2. Extract: schema-validated data, straight off the live DOM
const { stories } = await tab.extract(
  "extract the top 10 stories with their titles and points",
  z.object({
    stories: z.array(
      z.object({ title: z.string(), points: z.number() }),
    ),
  }),
)

// 3. Process: it's a real computer, so just write the file
await box.files.write({
  path: "data/frontpage.json",
  content: JSON.stringify(stories, null, 2),
})

// 4. Commit: git is right there too
await box.git.commit({ message: "Add today's front page" })
```

No second service, no credentials passed between environments, no glue. The browser and the filesystem and git are the same machine.

And the last step can be more than a commit. The box has a coding agent, so you can hand the scraped data to it and let it do the work:

```typescript
await box.agent.run({
  prompt:
    "Read data/frontpage.json, keep the stories above 100 points, " +
    "and write a short markdown digest to digest.md.",
})
```

Scrape with the browser, reason with the agent, ship with git. One box.

---

## Try it

Create a box with `browser: true`, open a tab, and read a page. That is the whole setup. From there the extraction, the AI actions, and the CDP connection are one call away.

The [browser guide](https://upstash.com/docs/box/overall/browser/overview) walks through each piece. The free tier is enough to try it, so grab an API key from the [Upstash Console](https://console.upstash.com), and see how much of your scraping-and-then-something workflow fits in a single box.

We'd love to hear what you build. Reach out anytime on [Discord](https://upstash.com/discord).