home / skills / openclaw / skills / playwright-npx

playwright-npx skill

/skills/mahone-bot/playwright-npx

This skill enables automated browser tasks with Playwright and Node.js, handling JavaScript-heavy sites, complex interactions, and reusable scripting workflows.

npx playbooks add skill openclaw/skills --skill playwright-npx

Review the files below or copy the command above to add this skill to your agents.

Files (12)
SKILL.md
6.4 KB
---
name: playwright-npx
description: Fast browser automation using Node.js scripts with Playwright (run via `node script.mjs`). Use for web scraping, screenshots, form automation, and any browser task requiring programmatic control. For simple page fetching without JavaScript execution, use web_fetch first. For interactive CLI browsing without writing code, use browser tool or playwright-cli. This skill is ideal when you need full control, custom logic, or reusable scripts.
metadata: {"clawdbot":{"emoji":"🎭","requires":{"bins":["node","npx"]}}, "created_by": "Kuba + Mahone", "created_date": "2026-02-04", "is_custom": true}
---

# Playwright Browser Automation

> 🤝 Developed together by Kuba + Mahone · Feb 2026

Code-first browser automation with Playwright.

## When to Use

| Tool | Use When |
|------|----------|
| **web_fetch** | Simple pages, no JavaScript needed |
| **This skill** | JavaScript-heavy sites, complex interactions, full control |
| **stealth-browser** | Bot detection / Cloudflare issues |
| **browser tool** | Visual exploration, last resort |
| **playwright-cli** | Interactive CLI without writing code |

## Setup

```bash
# One-time per project
npm init -y
npm install playwright
npx playwright install chromium
```

**package.json example:**
```json
{
  "name": "my-automation",
  "type": "module",
  "dependencies": {
    "playwright": "^1.40.0"
  }
}
```

## Minimal Example

```javascript
// tmp/example.mjs
import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage();

await page.goto('https://example.com');
console.log('Title:', await page.title());

await browser.close();
```

```bash
node tmp/example.mjs
```

## Quick Patterns

### Screenshot
```javascript
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage();
await page.setViewportSize({ width: 1280, height: 800 });
await page.goto('https://example.com');
await page.screenshot({ path: 'tmp/screenshot.png', fullPage: true });
await browser.close();
```

### Scrape Data
```javascript
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://news.ycombinator.com');
const stories = await page.$$eval('.titleline > a', links => 
  links.slice(0, 5).map(a => ({ title: a.innerText, url: a.href }))
);
console.log(JSON.stringify(stories, null, 2));
await browser.close();
```

### Form Interaction
```javascript
await page.goto('https://example.com/login');
await page.fill('input[name="email"]', '[email protected]');
await page.fill('input[name="password"]', 'password');
await page.click('button[type="submit"]');
```

### Wait for Dynamic Content
```javascript
// Wait for network idle (SPA)
await page.goto(url, { waitUntil: 'networkidle' });

// Wait for specific element
await page.waitForSelector('.results', { timeout: 10000 });

// Wait for condition
await page.waitForFunction(() => 
  document.querySelectorAll('.item').length > 0
);
```

### Persistent Session
```javascript
import fs from 'fs';
const SESSION_FILE = 'tmp/session.json';

let context;
if (fs.existsSync(SESSION_FILE)) {
  context = await browser.newContext({ storageState: SESSION_FILE });
} else {
  context = await browser.newContext();
}
const page = await context.newPage();
// ... login ...
await context.storageState({ path: SESSION_FILE });
```

## Headless vs Headed

```javascript
// Headless (default, fastest)
await chromium.launch({ headless: true });

// Headed (see the browser)
await chromium.launch({ headless: false });

// Slow motion (debugging)
await chromium.launch({ headless: false, slowMo: 100 });
```

## Selectors Quick Reference

```javascript
// CSS
await page.click('button.submit');
await page.fill('input#email', 'text');

// Text content
await page.click('text=Submit');
await page.click('text=/log\s*in/i');  // regex

// XPath
await page.click('xpath=//button[@type="submit"]');

// ARIA role
await page.click('role=button[name="Submit"]');

// Test ID (most stable)
await page.click('[data-testid="submit-btn"]');

// Chain selectors
await page.click('nav >> text=Settings');
```

**See [references/selectors.md](references/selectors.md) for complete selector guide.**

## Error Handling

```javascript
try {
  await page.goto('https://example.com', { timeout: 30000 });
  const hasResults = await page.locator('.results').isVisible().catch(() => false);
  if (!hasResults) {
    console.log('No results');
    process.exit(0);
  }
} catch (error) {
  console.error('Error:', error.message);
  await page.screenshot({ path: 'tmp/error.png' });
  process.exit(1);
} finally {
  await browser.close();
}
```

## Examples & Templates

### Working Examples
- [examples/screenshot.mjs](examples/screenshot.mjs) - Full-page screenshots
- [examples/scrape.mjs](examples/scrape.mjs) - Data extraction
- [examples/form-interaction.mjs](examples/form-interaction.mjs) - Form automation
- [examples/login-session.mjs](examples/login-session.mjs) - Persistent sessions

### Reusable Templates
- [scripts/minimal-template.mjs](scripts/minimal-template.mjs) - Clean starting point
- [scripts/screenshot-template.mjs](scripts/screenshot-template.mjs) - Configurable screenshot
- [scripts/scrape-template.mjs](scripts/scrape-template.mjs) - Data scraping scaffold

**Copy templates:**
```bash
cp scripts/minimal-template.mjs tmp/my-task.mjs
# Edit tmp/my-task.mjs, then run:
node tmp/my-task.mjs
```

## Tooling Commands

```bash
# Record interactions to generate code
npx playwright codegen https://example.com

# Debug selectors
npx playwright codegen --target javascript https://example.com

# Show trace
npx playwright show-trace tmp/trace.zip
```

## Deep References

- **[references/selectors.md](references/selectors.md)** - Complete selector guide (CSS, text, XPath, ARIA, test-id)
- **[references/debugging.md](references/debugging.md)** - Debugging techniques (headless, slowMo, screenshots)
- **[references/troubleshooting.md](references/troubleshooting.md)** - Common errors and solutions

## Tips

- Always put scripts in `tmp/` — it's gitignored
- Use `.mjs` extension for ES modules (no `type: module` needed)
- Add `console.log()` liberally for debugging
- Use `page.screenshot()` when things go wrong
- For complex sites, add `await page.waitForLoadState('networkidle')`
- See [references/debugging.md](references/debugging.md) for detailed debugging guide
- See [references/troubleshooting.md](references/troubleshooting.md) for common issues

Overview

This skill provides code-first browser automation using Playwright run via node scripts (e.g., node script.mjs). It’s designed for JavaScript-heavy sites and tasks that need full programmatic control: scraping, screenshots, form automation, session persistence, and complex interactions. Use lighter tools when you don’t need JS execution or when you prefer interactive CLI browsing.

How this skill works

You write Node.js ES module scripts that import Playwright, launch a Chromium browser, create pages or contexts, and drive the page with navigation, selectors, clicks, fills, screenshots, and waits. Scripts run with node (example: node tmp/example.mjs). Session state can be saved and reused and headless/headed modes and debugging options are supported.

When to use it

  • Scraping sites that require JavaScript execution or SPA navigation
  • Automating form submissions, logins, and multi-step flows
  • Generating full-page screenshots or PDFs programmatically
  • Reusing custom browser logic across projects or CI jobs
  • When you need fine-grained control over waits, selectors, and context storage

Best practices

  • Keep scripts in a git-ignored tmp/ directory for easy iteration
  • Use ES modules (.mjs) and node to run scripts consistently
  • Prefer stable selectors (data-testid, ARIA roles) over brittle CSS/XPath
  • Save and reuse storageState for persistent sessions and authenticated flows
  • Add console.log and screenshots on error for faster debugging

Example use cases

  • Scrape the top 5 headlines from a JS-driven news site and output JSON
  • Take full-page screenshots of product pages for visual QA
  • Automate login + submission workflows and persist the session to reuse later
  • Record interactions with npx playwright codegen, paste into a .mjs script, and run with node
  • Wait for networkidle or specific selectors to reliably extract dynamically loaded content

FAQ

Do I always need Playwright for simple page fetches?

No. Use a lightweight fetch tool when the page does not require JavaScript execution. This skill is for pages that render or load data in the browser.

How do I debug flaky selectors or timing issues?

Run with headful mode (headless: false) and slowMo, use page.screenshot() on errors, and leverage waitForSelector, waitForLoadState('networkidle'), or waitForFunction for specific conditions.