How to Do Browser Automation with Playwright

Build a maintainable browser automation workflow with Playwright, TypeScript-ready JavaScript, reliable locators, secure authentication, and failure monitoring.

By Kelis

Founder

Engineering12 min read
Technical browser automation architecture showing Playwright controlling a portal with authentication, validation, logs, retries, and failure alerts.

Browser automation uses code to control a web browser: opening pages, entering data, clicking buttons, downloading files, and extracting results.

For technical teams, making a browser click is the easy part. The real work is building an automation that handles authentication, slow pages, unexpected data, interface changes, and failures without silently damaging business operations.

This guide uses Playwright with Node.js because it supports Chromium, Firefox, and WebKit while providing auto-waiting, browser isolation, tracing, and resilient locators.

If you are still deciding where browser automation fits, first read what browser automation is and when to use it.


1. Define the Workflow

Document the manual process before writing code:

  • What triggers the workflow?

  • Which pages does the user visit?

  • What data enters and leaves the system?

  • What proves each step succeeded?

  • Which actions require human approval?

  • What should happen after a failure?

Choose a task with repeatable steps and predictable inputs. The browser automation use cases guide offers practical examples such as portal checks, form submissions, and report downloads.

Check whether the platform offers an API first. APIs are generally more stable for direct data exchange. Use browser automation when the necessary action is only available through the interface.


2. Install Playwright

Create a Node.js project and install Playwright:

mkdir browser-automation
cd browser-automation
npm init -y
npm install playwright
npx playwright install chromium

Add "type": "module" to package.json, then create automation.js.

Store credentials in environment variables rather than source code:

PORTAL_URL=https://example.com/login
PORTAL_EMAIL=user@example.com
PORTAL_PASSWORD=your-password

Use a secrets manager in production. Never commit credentials, cookies, tokens, or authenticated browser-state files.


3. Build the First Workflow

This example logs into a portal, waits for the dashboard, extracts a value, and captures a screenshot if the workflow fails:

import { chromium } from "playwright";

const required = ["PORTAL_URL", "PORTAL_EMAIL", "PORTAL_PASSWORD"];

for (const name of required) {
  if (!process.env[name]) throw new Error(`Missing ${name}`);
}

const browser = await chromium.launch({ headless: true });
const context = await browser.newContext();
const page = await context.newPage();

try {
  await page.goto(process.env.PORTAL_URL, {
    waitUntil: "domcontentloaded",
    timeout: 30_000,
  });

  await page.getByLabel("Email").fill(process.env.PORTAL_EMAIL);
  await page.getByLabel("Password").fill(process.env.PORTAL_PASSWORD);

  await page.getByRole("button", { name: /sign in/i }).click();

  const dashboard = page.getByRole("heading", {
    name: /dashboard/i,
  });

  await dashboard.waitFor({ state: "visible" });

  const openOrders = await page
    .getByTestId("open-orders")
    .textContent();

  if (!openOrders) {
    throw new Error("Open-order value was not found");
  }

  console.log({ openOrders, completedAt: new Date().toISOString() });
} catch (error) {
  await page.screenshot({
    path: `failure-${Date.now()}.png`,
    fullPage: true,
  });

  throw error;
} finally {
  await context.close();
  await browser.close();
}

The selectors are examples and must match the target website.


4. Use Stable Locators

Avoid long CSS selectors and XPath expressions tied to the page structure:

page.locator("#app > div:nth-child(2) > button");

A small interface change can break that selector.

Playwright recommends user-facing locators and explicit contracts:

page.getByRole("button", { name: "Submit" });
page.getByLabel("Customer email");
page.getByTestId("invoice-status");

Prefer locators in this order:

  1. Role and accessible name

  2. Form label

  3. Stable text

  4. Test ID when you control the application

  5. CSS only when a stronger contract is unavailable

Do not add arbitrary delays such as waitForTimeout(5000). Wait for a meaningful condition: a URL change, visible heading, completed download, or expected response.


5. Handle Authentication Safely

Repeated UI login is slow and can trigger security controls. Playwright can save and reuse authenticated storageState, including cookies and local storage.

Treat this file as a secret because it may allow account impersonation. Exclude it from Git, encrypt it when stored, limit access, and rotate it when the session expires.

For multi-factor authentication, use an approved service account or a controlled manual session-bootstrap process. Do not attempt to bypass CAPTCHA, access restrictions, or platform security controls.


6. Prepare It for Production

A production workflow needs more than try/catch.

Add:

  • Structured logs with a workflow and run ID

  • Screenshots and traces on failure

  • Limited retries for temporary errors

  • Idempotency or duplicate-submission checks

  • Input and output validation

  • Timeouts for navigation and actions

  • Alerts after the final failed attempt

  • A dead-letter queue for jobs requiring review

  • Metrics for success rate and execution time

  • A manual recovery procedure

Retries should only repeat actions that are safe. Retrying a payment or form submission without an idempotency check can create duplicates.

For more production guidance, read how to build reliable browser automation.


7. Test and Deploy

Test the workflow with missing fields, expired sessions, slow pages, duplicate records, changed labels, and empty results.

Run it through a scheduler, queue worker, container, or serverless job based on duration and volume. Keep concurrency low until you understand the portal’s limits.

Browser automation is ongoing engineering. Monitor it, review failures, and update it when the target website changes.

SpidLabs helps teams design and build browser automation around real operational workflows. Visit SpidLabs when you need a technical implementation with built-in testing, monitoring, and human review.


Technical References


FAQ

What programming language is best for browser automation?

JavaScript or TypeScript with Playwright is a strong choice for web-focused teams. Playwright also supports Python, Java, and .NET.

Should browser automation run headless?

Use headed mode while developing and debugging. Headless mode is normally more practical for scheduled production execution.

How should login sessions be stored?

Use encrypted secrets and protected browser-state files. Never commit passwords, cookies, tokens, or authentication state to Git.

How do you prevent duplicate submissions?

Use a unique workflow key, check the destination before submission, and record completed actions in a database or job store.

When should browser automation not be used?

Avoid it when a reliable API exists, the workflow requires unsupported security bypasses, or the task depends heavily on human judgment.