Browser
Source:
src/Cloudflare/Browser/Browser.ts
A Cloudflare Browser Rendering binding for launching headless browser
sessions from Workers via @cloudflare/puppeteer.
Effect-style Worker (recommended)
Section titled “Effect-style Worker (recommended)”Yielding the marker attaches the binding to the surrounding Worker and
returns the runtime {@link BrowserClient}. Every cf.BrowserRun method is
mirrored as an Effect, so no Effect.tryPromise wrapping is needed.
import * as Effect from "effect/Effect";
Cloudflare.Worker( "BrowserWorker", { main: import.meta.filename }, Effect.gen(function* () { const browser = yield* Cloudflare.Browser({ name: "BROWSER" });
return { fetch: Effect.gen(function* () { return yield* browser.markdown({ url: "https://example.com" }); }), }; }).pipe(Effect.provide(Cloudflare.BrowserBindingLive)),);Quick Actions
Section titled “Quick Actions”Render content, screenshot, PDF, and structured data
JSON quick actions resolve to their parsed payload; binary actions
(screenshot, pdf) resolve to a Stream of bytes. No Promise or
Response.json() in sight.
import * as Effect from "effect/Effect";import * as Stream from "effect/Stream";
const browser = yield* Cloudflare.Browser({ name: "BROWSER" });const url = "https://example.com";
// HTML content — parsed payload, title lives in `meta`.const content = yield* browser.content({ url });const title = content.meta.title;
// Scrape elements by CSS selector.yield* browser.scrape({ url, elements: [{ selector: "h1" }] });
// Extract all links.const { result: links } = yield* browser.links({ url });
// Binary actions stream bytes — collect or pipe them.const png = yield* browser.screenshot({ url }).pipe(Stream.runCollect);const pdf = yield* browser.pdf({ url }).pipe(Stream.runCollect);
// AI-extracted structured data.yield* browser.json({ url, prompt: "Extract the page heading" });Call the generic quickAction directly
The named methods are thin wrappers over quickAction, which mirrors
cf.BrowserRun["quickAction"] one-to-one.
const browser = yield* Cloudflare.Browser({ name: "BROWSER" });
const res = yield* browser.quickAction("snapshot", { url: "https://example.com",});Driving Puppeteer
Section titled “Driving Puppeteer”raw hands you the underlying runtime binding. Puppeteer is promise-based,
so this is the one place you reach for Effect.tryPromise.
import puppeteer from "@cloudflare/puppeteer";import * as Effect from "effect/Effect";
Cloudflare.Worker( "BrowserWorker", { main: import.meta.filename }, Effect.gen(function* () { const browser = yield* Cloudflare.Browser({ name: "BROWSER" });
return { fetch: Effect.gen(function* () { const binding = yield* browser.raw; const session = yield* Effect.tryPromise(() => puppeteer.launch(binding), ); try { const page = yield* Effect.tryPromise(() => session.newPage()); yield* Effect.tryPromise(() => page.goto("https://example.com")); const title = yield* Effect.tryPromise(() => page.title()); return Response.json({ title }); } finally { yield* Effect.promise(() => session.close()); } }), }; }).pipe(Effect.provide(Cloudflare.BrowserBindingLive)),);Worker binding metadata
Section titled “Worker binding metadata”Declare the binding on env
export const Worker = Cloudflare.Worker("Worker", { main: "./src/worker.ts", env: { BROWSER: Cloudflare.Browser(), },});
export type WorkerEnv = Cloudflare.InferEnv<typeof Worker>;// { BROWSER: BrowserRun }Async-style worker with the raw runtime binding
import puppeteer from "@cloudflare/puppeteer";import type { WorkerEnv } from "../alchemy.run.ts";
export default { async fetch(request: Request, env: WorkerEnv) { const browser = await puppeteer.launch(env.BROWSER); const page = await browser.newPage(); await page.goto("https://example.com"); const screenshot = await page.screenshot(); await browser.close();
return new Response(screenshot, { headers: { "content-type": "image/png" }, }); },};