Puppeteer
01 / 02

Browser & Page Fundamentals

Browser & Page Fundamentals

Launching & Navigating

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    headless: true,        // 'new' headless matches headful rendering closely
    slowMo: 0,              // ms delay between actions — useful for debugging with headless: false
    args: ['--no-sandbox'], // often needed in Docker/CI
  });

  const page = await browser.newPage();
  await page.setViewport({ width: 1280, height: 800 });

  await page.goto('https://example.com', {
    waitUntil: 'networkidle0', // 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'
    timeout: 30_000,
  });

  console.log(await page.title());

  await browser.close(); // always close — orphaned Chromium processes pile up otherwise
})();

// Multiple tabs in one browser process
const [pageA, pageB] = await Promise.all([browser.newPage(), browser.newPage()]);
await Promise.all([pageA.goto('https://a.com'), pageB.goto('https://b.com')]);

// Isolated session (own cookies/storage) without a new browser process
const context = await browser.createBrowserContext();
const isolatedPage = await context.newPage();

Selecting & Reading Content

// Single element — read text
const heading = await page.$eval('h1', el => el.textContent);

// All matching elements — scrape a list
const titles = await page.$$eval('.article-card h2', els =>
  els.map(el => el.textContent.trim())
);

// ElementHandle — reference to a live DOM node
const button = await page.$('#submit');
if (button) await button.click();

// Read an input's value (values aren't exposed by $() directly)
const email = await page.$eval('#email', el => el.value);

// Full rendered HTML (after JS has run — not a raw fetch of the URL)
const html = await page.content();

// Run arbitrary JS in the page and get a serializable result back
const count = await page.evaluate(() => document.querySelectorAll('li').length);

// Pass Node values into the page — must go through evaluate's extra args
const threshold = 10;
const matches = await page.evaluate(
  (min) => [...document.querySelectorAll('.price')].filter(el => Number(el.textContent) > min).length,
  threshold,
);

Forms, Clicks & Screenshots

await page.type('#email', 'user@example.com');   // dispatches real key events, triggers input handlers
await page.select('#country', 'US');              // matches <option value>
await page.click('#submit');                      // waits for visible + unobstructed, clicks center

// Click that triggers navigation — await both together, not sequentially
await Promise.all([
  page.waitForNavigation(),
  page.click('a.next-page'),
]);

// Screenshots
await page.screenshot({ path: 'viewport.png' });
await page.screenshot({ path: 'full.png', fullPage: true });
await page.screenshot({ path: 'crop.png', clip: { x: 0, y: 0, width: 400, height: 300 } });

// PDF (headless only)
await page.pdf({ path: 'page.pdf', format: 'A4', printBackground: true });

// Waiting for a selector before interacting — avoid fixed sleeps, they're flaky
await page.waitForSelector('.results-loaded', { timeout: 10_000 });

// File upload
const [fileChooser] = await Promise.all([
  page.waitForFileChooser(),
  page.click('#upload-button'),
]);
await fileChooser.accept(['./photo.png']);

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free