playwright vs puppeteer vs selenium-webdriver
Browser Automation and End-to-End Testing in JavaScript
playwrightpuppeteerselenium-webdriverSimilar Packages:

Browser Automation and End-to-End Testing in JavaScript

playwright, puppeteer, and selenium-webdriver are all browser automation libraries used primarily for end-to-end testing, web scraping, and automated UI interaction. They allow developers to control real browsers programmatically, simulate user actions, and verify application behavior. While they share similar goals, they differ significantly in architecture, supported browsers, API design, and ecosystem maturity.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
playwright089,5074.91 MB16615 days agoApache-2.0
puppeteer094,37240.2 kB26212 hours agoApache-2.0
selenium-webdriver034,11718.1 MB19314 days agoApache-2.0

Browser Automation Showdown: Playwright vs Puppeteer vs Selenium WebDriver

When you need to automate real browsers — whether for end-to-end tests, visual regression, or scraping dynamic sites — three major players dominate the JavaScript ecosystem: Playwright, Puppeteer, and Selenium WebDriver. All let you script browser interactions, but their architectures, capabilities, and developer experiences diverge sharply. Let’s compare them head-to-head.

🌐 Browser Support: One Engine vs Three vs Everything

playwright supports three rendering engines out of the box: Chromium, Firefox, and WebKit (Safari’s engine). This means you can test how your app behaves across all major desktop browsers using a single API.

// playwright: Switch browsers with one config change
const { chromium, firefox, webkit } = require('playwright');

const browser = await firefox.launch(); // or chromium / webkit
const page = await browser.newPage();
await page.goto('https://example.com');

puppeteer only controls Chromium-based browsers (Chrome, Edge, Chromium). There’s no official support for Firefox or Safari.

// puppeteer: Chromium-only
const puppeteer = require('puppeteer');

const browser = await puppeteer.launch(); // always Chromium
const page = await browser.newPage();
await page.goto('https://example.com');

selenium-webdriver supports virtually any browser via vendor-provided drivers (ChromeDriver, GeckoDriver, etc.). You can even test legacy IE if needed — though it requires separate driver binaries and complex setup.

// selenium-webdriver: Explicit driver per browser
const { Builder, By, until } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');

let driver = await new Builder()
  .forBrowser('chrome')
  .setChromeOptions(new chrome.Options())
  .build();

await driver.get('https://example.com');

💡 Key takeaway: Need WebKit or Firefox? Only Playwright delivers first-party, zero-config support. Selenium gives broader coverage but at the cost of complexity.

⏳ Waiting and Reliability: Auto-Wait vs Manual Sync

Flaky tests often stem from poor synchronization — clicking before an element loads, for example. How each library handles this is critical.

playwright has built-in auto-waiting. Methods like click(), fill(), and goto() automatically wait for elements to be actionable (visible, enabled, stable).

// playwright: No explicit waits needed
await page.goto('/login');
await page.fill('#email', 'user@example.com'); // Waits for #email to be ready
await page.click('#submit'); // Waits for button to be clickable

puppeteer requires manual waiting. You must use waitForSelector(), waitForFunction(), or similar to avoid race conditions.

// puppeteer: Manual sync required
await page.goto('/login');
await page.waitForSelector('#email'); // Must wait explicitly
await page.type('#email', 'user@example.com');
await page.click('#submit');

selenium-webdriver also lacks auto-waiting. You rely on WebDriverWait with expected conditions — but note: the JavaScript version doesn’t have a direct equivalent; you typically use driver.wait() with custom conditions.

// selenium-webdriver: Explicit waits
await driver.get('/login');
await driver.wait(until.elementLocated(By.id('email')), 5000);
await driver.findElement(By.id('email')).sendKeys('user@example.com');
await driver.findElement(By.id('submit')).click();

💡 Key takeaway: Playwright reduces flakiness by default. With Puppeteer or Selenium, you’ll write more boilerplate to achieve the same reliability.

🧪 Test Isolation and Emulation: Contexts vs Pages vs Sessions

Isolating tests prevents state leakage (e.g., cookies from one test affecting another).

playwright uses browser contexts — lightweight, isolated environments that share a browser instance but have separate cookies, storage, and permissions. Creating a new context is fast (~1ms).

// playwright: Per-test context
const browser = await chromium.launch();
const context = await browser.newContext(); // Isolated session
const page = await context.newPage();
// ... test logic
await context.close(); // Clean up instantly

puppeteer relies on separate pages or browser instances for isolation. Pages share cookies/storage unless you launch a new browser (which is slow) or use incognito mode (less convenient).

// puppeteer: Incognito for isolation
const browser = await puppeteer.launch();
const context = await browser.createIncognitoBrowserContext();
const page = await context.newPage();
// ... test logic
await context.close();

selenium-webdriver typically uses one driver per test, meaning you launch/quit the entire browser for each test — which is slow and resource-heavy.

// selenium-webdriver: Full browser per test
let driver = await new Builder().forBrowser('chrome').build();
// ... test logic
await driver.quit(); // Entire browser shuts down

💡 Key takeaway: Playwright’s context model enables fast, truly isolated tests. Others force trade-offs between speed and cleanliness.

🎥 Debugging and Observability: Built-in Tooling

When tests fail, you need visibility.

playwright includes trace viewer, video recording, and screenshot-on-failure out of the box. You can replay every action with DOM snapshots, network logs, and console output.

// playwright.config.js
module.exports = {
  use: {
    trace: 'on-first-retry',
    video: 'on-first-retry',
    screenshot: 'only-on-failure'
  }
};

puppeteer offers basic screenshot/PDF APIs but no integrated debugging suite. You’d need third-party tools for traces or videos.

// puppeteer: Manual screenshots only
await page.screenshot({ path: 'error.png' });

selenium-webdriver provides no built-in media capture. You must implement screenshot logic yourself and integrate external tools for video or tracing.

// selenium-webdriver: DIY screenshots
const fs = require('fs');
const image = await driver.takeScreenshot();
fs.writeFileSync('error.png', image, 'base64');

💡 Key takeaway: Playwright ships with a full observability stack. The others leave you to build your own.

🧰 Architecture and Setup Complexity

playwright bundles its own browser binaries. Installation is one command (npm install playwright), and it works offline. No external dependencies.

puppeteer also bundles Chromium by default (npm install puppeteer), so setup is equally simple — but only for Chromium.

selenium-webdriver does not bundle drivers. You must separately install ChromeDriver, GeckoDriver, etc., and manage version compatibility. This adds operational overhead.

# selenium-webdriver requires manual driver setup
npm install selenium-webdriver
# Then download matching chromedriver.exe manually or via webdriver-manager

💡 Key takeaway: Playwright and Puppeteer “just work.” Selenium demands infra maintenance.

🔁 Real-World Example: Login Flow

Let’s compare a realistic login test across all three.

Playwright (concise, reliable)

const { test, expect } = require('@playwright/test');

test('login succeeds', async ({ page }) => {
  await page.goto('/login');
  await page.fill('#email', 'user@test.com');
  await page.fill('#password', 'secret');
  await page.click('#submit');
  await expect(page.locator('#welcome')).toBeVisible();
});

Puppeteer (requires explicit waits)

const puppeteer = require('puppeteer');

it('login succeeds', async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('/login');
  await page.waitForSelector('#email');
  await page.type('#email', 'user@test.com');
  await page.type('#password', 'secret');
  await page.click('#submit');
  await page.waitForSelector('#welcome');
  const isVisible = await page.$eval('#welcome', el => el.offsetParent !== null);
  expect(isVisible).toBe(true);
  await browser.close();
});

Selenium WebDriver (verbose, driver-dependent)

const { Builder, By, until } = require('selenium-webdriver');

it('login succeeds', async () => {
  let driver = await new Builder().forBrowser('chrome').build();
  try {
    await driver.get('/login');
    await driver.wait(until.elementLocated(By.id('email')), 5000);
    await driver.findElement(By.id('email')).sendKeys('user@test.com');
    await driver.findElement(By.id('password')).sendKeys('secret');
    await driver.findElement(By.id('submit')).click();
    await driver.wait(until.elementLocated(By.id('welcome')), 5000);
    const isDisplayed = await driver.findElement(By.id('welcome')).isDisplayed();
    expect(isDisplayed).toBe(true);
  } finally {
    await driver.quit();
  }
});

📊 Summary: When to Use Which

Concernplaywrightpuppeteerselenium-webdriver
Browser Coverage✅ Chromium, Firefox, WebKit❌ Chromium only✅ Any (with drivers)
Auto-Waiting✅ Built-in❌ Manual❌ Manual
Test Isolation✅ Fast contexts⚠️ Incognito or new browser❌ Full browser per test
Debugging Tools✅ Trace, video, screenshots⚠️ Screenshots only❌ DIY
Setup Simplicity✅ Bundled browsers✅ Bundled Chromium❌ External drivers required
Enterprise Integration⚠️ Growing❌ Limited✅ Mature (Grid, cross-language)

💡 Final Recommendation

  • Start with Playwright if you’re building a new test suite. Its cross-browser support, reliability, and tooling make it the most future-proof choice for modern web apps.
  • Stick with Puppeteer only if you’re already invested in Chromium-only workflows (e.g., PDF generation) and don’t need multi-browser coverage.
  • Use Selenium WebDriver only if you’re locked into a Selenium Grid, must support legacy browsers, or have cross-language test requirements.

All three are actively maintained and production-ready — but Playwright represents the current state of the art for JavaScript-based browser automation.

How to Choose: playwright vs puppeteer vs selenium-webdriver

  • playwright:

    Choose playwright if you need cross-browser support (Chromium, Firefox, WebKit), built-in auto-waiting, reliable test isolation with context-level emulation, and modern tooling like trace viewer and video recording. It’s ideal for teams building robust, maintainable E2E test suites that must work consistently across multiple browsers without flakiness.

  • puppeteer:

    Choose puppeteer if your testing or automation needs are focused exclusively on Chromium-based browsers (Chrome, Edge) and you want a lightweight, fast-starting solution with tight integration into the Chrome DevTools Protocol. It works well for PDF generation, screenshot capture, or internal tooling where multi-browser support isn’t required.

  • selenium-webdriver:

    Choose selenium-webdriver if you must integrate with an existing Selenium Grid infrastructure, require language interoperability (Java, Python, C#, etc.), or need to run tests against legacy browser versions not supported by newer tools. It’s best suited for enterprise environments with established Selenium workflows and cross-language test suites.

README for playwright

🎭 Playwright

npm version Chromium version Firefox version WebKit version Join Discord

Documentation | API reference

Playwright is a framework for web automation and testing. It drives Chromium, Firefox, and WebKit with a single API — in your tests, in your scripts, and as a tool for AI agents.

Get Started

Choose the path that fits your workflow:

Best forInstall
Playwright TestEnd-to-end testingnpm init playwright@latest
Playwright CLICoding agents (Claude Code, Copilot)npm i -g @playwright/cli@latest
Playwright MCPAI agents and LLM-driven automationnpx @playwright/mcp@latest
Playwright LibraryBrowser automation scriptsnpm i playwright
VS Code ExtensionTest authoring and debugging in VS CodeInstall from Marketplace

Playwright Test

Playwright Test is a full-featured test runner built for end-to-end testing. It runs tests across Chromium, Firefox, and WebKit with full browser isolation, auto-waiting, and web-first assertions.

Install

npm init playwright@latest

Or add manually:

npm i -D @playwright/test
npx playwright install

Write a test

import { test, expect } from '@playwright/test';

test('has title', async ({ page }) => {
  await page.goto('https://playwright.dev/');
  await expect(page).toHaveTitle(/Playwright/);
});

test('get started link', async ({ page }) => {
  await page.goto('https://playwright.dev/');
  await page.getByRole('link', { name: 'Get started' }).click();
  await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
});

Run tests

npx playwright test

Tests run in parallel across all configured browsers, in headless mode by default. Each test gets a fresh browser context — full isolation with near-zero overhead.

Key capabilities

Auto-wait and web-first assertions. No artificial timeouts. Playwright waits for elements to be actionable, and assertions automatically retry until conditions are met.

Locators. Find elements with resilient locators that mirror how users see the page:

page.getByRole('button', { name: 'Submit' })
page.getByLabel('Email')
page.getByPlaceholder('Search...')
page.getByTestId('login-form')

Test isolation. Each test runs in its own browser context — equivalent to a fresh browser profile. Save authentication state once and reuse it across tests:

// Save state after login
await page.context().storageState({ path: 'auth.json' });

// Reuse in other tests
test.use({ storageState: 'auth.json' });

Tracing. Capture execution traces, screenshots, and videos on failure. Inspect every action, DOM snapshot, network request, and console message in the Trace Viewer:

// playwright.config.ts
export default defineConfig({
  use: {
    trace: 'on-first-retry',
  },
});
npx playwright show-trace trace.zip

Parallelism. Tests run in parallel by default across all configured browsers.

Full testing documentation


Playwright CLI

Playwright CLI is a command-line interface for browser automation designed for coding agents. It's more token-efficient than MCP — commands avoid loading large tool schemas and accessibility trees into the model context.

Install

npm install -g @playwright/cli@latest

Optionally install skills for richer agent integration:

playwright-cli install --skills

Usage

Point your coding agent at a task:

Test the "add todo" flow on https://demo.playwright.dev/todomvc using playwright-cli.
Take screenshots for all successful and failing scenarios.

Or run commands directly:

playwright-cli open https://demo.playwright.dev/todomvc/ --headed
playwright-cli type "Buy groceries"
playwright-cli press Enter
playwright-cli screenshot

Session monitoring

Use playwright-cli show to open a visual dashboard with live screencast previews of all running browser sessions. Click any session to zoom in and take remote control.

playwright-cli show

Full CLI documentation | GitHub


Playwright MCP

The Playwright MCP server gives AI agents full browser control through the Model Context Protocol. Agents interact with pages using structured accessibility snapshots — no vision models or screenshots required.

Setup

Add to your MCP client (VS Code, Cursor, Claude Desktop, Windsurf, etc.):

{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["@playwright/mcp@latest"]
    }
  }
}

One-click install for VS Code:

Install in VS Code

For Claude Code:

claude mcp add playwright npx @playwright/mcp@latest

How it works

Ask your AI assistant to interact with any web page:

Navigate to https://demo.playwright.dev/todomvc and add a few todo items.

The agent sees the page as a structured accessibility tree:

- heading "todos" [level=1]
- textbox "What needs to be done?" [ref=e5]
- listitem:
  - checkbox "Toggle Todo" [ref=e10]
  - text: "Buy groceries"

It uses element refs like e5 and e10 to click, type, and interact — deterministically and without visual ambiguity. Tools cover navigation, form filling, screenshots, network mocking, storage management, and more.

Full MCP documentation | GitHub


Playwright Library

Use playwright as a library for browser automation scripts — web scraping, PDF generation, screenshot capture, and any workflow that needs programmatic browser control without a test runner.

Install

npm i playwright

Examples

Take a screenshot:

import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://playwright.dev/');
await page.screenshot({ path: 'screenshot.png' });
await browser.close();

Generate a PDF:

import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://playwright.dev/');
await page.pdf({ path: 'page.pdf', format: 'A4' });
await browser.close();

Emulate a mobile device:

import { chromium, devices } from 'playwright';

const browser = await chromium.launch();
const context = await browser.newContext(devices['iPhone 15']);
const page = await context.newPage();
await page.goto('https://playwright.dev/');
await page.screenshot({ path: 'mobile.png' });
await browser.close();

Intercept network requests:

import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage();
await page.route('**/*.{png,jpg,jpeg}', route => route.abort());
await page.goto('https://playwright.dev/');
await browser.close();

Library documentation | API reference


VS Code Extension

The Playwright VS Code extension brings test running, debugging, and code generation directly into your editor.

Run and debug tests from the editor with a single click. Set breakpoints, inspect variables, and step through test execution with a live browser view.

Generate tests with CodeGen. Click "Record new" to open a browser — navigate and interact with your app while Playwright writes the test code for you.

Pick locators. Hover over any element in the browser to see the best available locator, then click to copy it to your clipboard.

Trace Viewer integration. Enable "Show Trace Viewer" in the sidebar to get a full execution trace after each test run — DOM snapshots, network requests, console logs, and screenshots at every step.

Install the extension | VS Code guide


Cross-Browser Support

LinuxmacOSWindows
Chromium1 148.0.7778.96:white_check_mark::white_check_mark::white_check_mark:
WebKit 26.4:white_check_mark::white_check_mark::white_check_mark:
Firefox 150.0.2:white_check_mark::white_check_mark::white_check_mark:

Headless and headed execution on all platforms. 1 Uses Chrome for Testing by default.

Other Languages

Playwright is also available for Python, .NET, and Java.

Resources