Playwright is an open-source library from Microsoft for controlling web browsers from code. A Playwright script starts a browser, opens pages, clicks, types, waits for things to appear and reads what is on the screen — the same things a person does, only repeatable and fast. Its own site describes it as enabling "reliable web automation for testing, scripting, and AI agents", with "one API to drive Chromium, Firefox, and WebKit".
This page explains what Playwright is made of and what it is good for, then runs a first test and a first script, with the install sizes and times we measured.
How we tested. On 2026-11-08 on Linux: @playwright/test 1.64.1 from npm (1.65.0 was released the same week; nothing below depends on the difference) and the Python package 1.64.1, against the public playwright.dev site.
Where it comes from
Playwright is a Microsoft project: its repository was created in November 2019 and version 1.0 reached npm on 6 May 2020. It is licensed Apache-2.0 and developed in the open at github.com/microsoft/playwright. Each release is pinned to specific browser builds, listed in the package's browsers.json.
What is in the box
- One API, three browser engines. Chromium (Chrome and Edge), Firefox and WebKit (the engine of Safari). Playwright downloads its own tested build of each instead of driving whatever is installed.
- Four languages. TypeScript/JavaScript, Python, .NET and Java, with the same concepts and nearly the same method names.
- A test runner (
@playwright/testin JavaScript): parallel workers, retries, fixtures, HTML reports, and assertions that wait (expect(page).toHaveTitle(...)retries until it passes or times out). Python uses pytest with a plugin instead. - Auto-waiting. Before a click, Playwright waits until the element is attached, visible, stable, enabled and not covered, which removes most
sleepcalls from test code (actionability). - Locators.
page.getByRole('link', { name: 'Get started' })finds elements the way a user or a screen reader would, by role and accessible name, rather than by CSS paths that break with every redesign. - Tools.
npx playwright codegenrecords your clicks into code (codegen); the trace viewer replays a failed test step by step with DOM snapshots and network calls (trace viewer). - Device emulation. 207 device descriptors in 1.64 (iPhones, Pixels, Galaxy phones, iPads), each a viewport, pixel ratio, touch and user agent (mobile screenshots compares them with ours).
A first test
npm init -y
npm i -D @playwright/test
npx playwright install chromium
// tests/example.spec.ts
import { test, expect } from '@playwright/test';
test('docs home has a title and a working Get started link', async ({ page }) => {
await page.goto('https://playwright.dev/');
await expect(page).toHaveTitle(/Playwright/);
await page.getByRole('link', { name: 'Get started' }).click();
await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
});
test('screenshot of the first screen', async ({ page }) => {
await page.goto('https://playwright.dev/');
await page.screenshot({ path: 'playwright-dev.png' });
});
$ npx playwright test
Running 2 tests using 1 worker
✓ 1 [chromium] › tests/example.spec.ts:3:5 › docs home has a title and a working Get started link (866ms)
✓ 2 [chromium] › tests/example.spec.ts:10:5 › screenshot of the first screen (512ms)
2 passed (2.3s)
With a playwright.config.ts limited to the Chromium project, the whole run took 3.1 s including Node's start. No waits in the code: click() waited for the link to be clickable, toBeVisible() for the next page's heading.
A first script in Python
The same API without a test runner, for automation rather than testing:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://playwright.dev/")
print(page.title())
page.screenshot(path="playwright-dev.png")
browser.close()
Installed with pip install playwright and playwright install chromium, it printed "Fast and reliable end-to-end testing for modern web apps | Playwright" and saved the screenshot in 2.1 s.
What it costs to run
Nothing in money, but some disk and upkeep:
| Part | Size on disk (Linux, 1.64.1) |
|---|---|
@playwright/test with its dependencies |
19 MB |
Chrome for Testing 156 (chromium-1248) |
403 MB |
| Chrome Headless Shell 156, used for headless runs | 273 MB |
| ffmpeg build for video recording | 5 MB |
Firefox and WebKit add their own builds if you install them. Each Playwright release pins its own browser builds, so updating Playwright usually means downloading browsers again — in CI, cache ~/.cache/ms-playwright between runs. On Linux, npx playwright install --with-deps also installs the system libraries the browsers need.
Playwright, Puppeteer and Selenium
All three drive real browsers; they differ in scope. Playwright vs Puppeteer measures the same jobs in both (Puppeteer is Chrome-first and JavaScript-only, with a smaller API and no test runner). Selenium vs Puppeteer covers Selenium, which speaks the W3C WebDriver standard to every major browser and has the widest language support. For a new test suite, Playwright's waiting model and bundled runner are the main reasons to start with it.
When you only need the pictures
A common use of Playwright outside testing is taking screenshots of pages: for previews, thumbnails, monitoring or social images. If that is the whole job, running browsers for it is optional: a screenshot API does the capture and returns the file. Ours waits for the page, removes cookie banners on request, emulates devices and returns PNG, JPEG, WebP, PDF or a scroll video; a capture costs 1 render of the quota (screenshot API, Playwright screenshots for doing it yourself). For tests of your own app, logged-in flows and anything interactive, Playwright itself is the right tool.