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

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 chromiumAdd "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-passwordUse 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:
Role and accessible name
Form label
Stable text
Test ID when you control the application
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.
