Fork of Playwright that was modified to be compatible with Cloudflare Workers and Browser Run (formerly Browser Rendering).
🏷️ Upstream Playwright version: 1.58.2
Create a Cloudflare Worker
npm create cloudflare@latest -- cf-playwright-workerThis package is released on npmjs.com at @cloudflare/playwright. To install it in your project:
npm i -s @cloudflare/playwright📄 Place this in wrangler.toml
compatibility_flags = [ "nodejs_compat" ]
browser = { binding = "MYBROWSER" }Browser Run now has full CDP support,
so starting with @cloudflare/playwright version 1.3.0, the library uses the
standard CDP (Chrome DevTools Protocol) internally to communicate with Browser
Run.
Everything should work the same way, but if you encounter any issues, please
report them. You can also
downgrade to a previous playwright version by using compatibility_date prior to
2026-03-17 or by adding the no_websocket_standard_binary_type flag:
compatibility_date = "2026-03-16"
# or
compatibility_flags = ["nodejs_compat", "no_websocket_standard_binary_type", ...]See cloudflare/puppeteer#193 and cloudflare/workerd#6442 for details.
You can find a full running example here Cloudflare Playwright running example
import { launch } from '@cloudflare/playwright';
export default {
async fetch(request, env): Promise<Response> {
const browser = await launch(env.MYBROWSER);
const page = await browser.newPage();
await page.goto('https://demo.playwright.dev/todomvc');
const TODO_ITEMS = [
'buy some cheese',
'feed the cat',
'book a doctors appointment'
];
const newTodo = page.getByPlaceholder('What needs to be done?');
for (const item of TODO_ITEMS) {
await newTodo.fill(item);
await newTodo.press('Enter');
}
const img = await page.screenshot();
await browser.close();
return new Response(img, {
headers: {
'Content-Type': 'image/png',
},
});
}
} satisfies ExportedHandler<Env>;import fs from "fs";
import { launch } from "@cloudflare/playwright";
export default {
async fetch(request, env): Promise<Response> {
const browser = await launch(env.MYBROWSER);
const page = await browser.newPage();
await page.context().tracing.start({ screenshots: true, snapshots: true });
// ... do something, screenshot for example
// For now, fs only supports writing into /tmp
await page.context().tracing.stop({ path: "/tmp/trace.zip" });
await browser.close();
const file = await fs.promises.readFile("/tmp/trace.zip");
return new Response(file, {
status: 200,
headers: {
"Content-Type": "application/zip",
},
});
}
} satisfies ExportedHandler<Env>;import { launch } from "@cloudflare/playwright";
import { expect } from "@cloudflare/playwright/test";
export default {
async fetch(request, env): Promise<Response> {
const browser = await launch(env.MYBROWSER);
const page = await browser.newPage();
await page.goto("https://demo.playwright.dev/todomvc");
const TODO_ITEMS = [
"buy some cheese",
"feed the cat",
"book a doctors appointment",
];
const newTodo = page.getByPlaceholder("What needs to be done?");
for (const item of TODO_ITEMS) {
await newTodo.fill(item);
await newTodo.press("Enter");
}
await expect(page.getByTestId("todo-title")).toHaveCount(TODO_ITEMS.length);
await Promise.all(
TODO_ITEMS.map((value, index) =>
expect(page.getByTestId("todo-title").nth(index)).toHaveText(value)
)
);
}
} satisfies ExportedHandler<Env>;To build Playwright for Cloudflare:
npm ci
git submodule update --init
cd packages/playwright-cloudflare
npm run buildTo run the TodoMVC example:
- launch it with
wrangler:
cd packages/playwright-cloudflare/examples/todomvc
npm ci
npx wrangler dev --remote- press
bto open the browser
The following capabilities are not fully supported, but we’re actively working on them.
- Playwright Test except Assertions
- Components
- Firefox, Android and Electron, as well as different versions of Chrome
- Videos
This is not an exhaustive list — expect rapid changes as we work toward broader parity with the original feature set. You can also check latest test results for a granular up to date list of the features that are fully supported