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