Table of contents

How to Handle Multiple Tabs and Windows in Puppeteer?

Managing multiple tabs and windows is a common requirement in web scraping and automation workflows. Puppeteer provides robust APIs to create, manage, and interact with multiple browser contexts, making it easy to handle complex multi-tab scenarios efficiently.

Understanding Pages vs Tabs in Puppeteer

In Puppeteer, each tab or window is represented as a Page object. When you launch a browser, you get a Browser instance that can create multiple pages. Each page operates independently with its own DOM, JavaScript context, and state.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: false });

  // Get all pages (tabs) currently open
  const pages = await browser.pages();
  console.log(`Number of open tabs: ${pages.length}`);

  await browser.close();
})();

Creating New Tabs and Windows

Opening New Tabs

The most common way to create a new tab is using the browser.newPage() method:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: false });

  // Create multiple new tabs
  const page1 = await browser.newPage();
  const page2 = await browser.newPage();
  const page3 = await browser.newPage();

  // Navigate each tab to different URLs
  await page1.goto('https://example.com');
  await page2.goto('https://google.com');
  await page3.goto('https://github.com');

  // Wait for a few seconds to see the tabs
  await new Promise(resolve => setTimeout(resolve, 3000));

  await browser.close();
})();

Opening New Windows

To open a new browser window instead of a tab, you can create a new browser context:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: false });

  // Create a new browser context (new window)
  const context = await browser.createIncognitoBrowserContext();
  const page = await context.newPage();

  await page.goto('https://example.com');

  // The original browser window is still available
  const originalPage = await browser.newPage();
  await originalPage.goto('https://google.com');

  await new Promise(resolve => setTimeout(resolve, 3000));

  await context.close();
  await browser.close();
})();

Managing Multiple Pages

Listing All Open Pages

You can get a list of all open pages and iterate through them:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: false });

  // Create several pages
  const page1 = await browser.newPage();
  const page2 = await browser.newPage();
  const page3 = await browser.newPage();

  await page1.goto('https://example.com');
  await page2.goto('https://google.com');
  await page3.goto('https://github.com');

  // Get all pages
  const pages = await browser.pages();

  // Iterate through all pages
  for (let i = 0; i < pages.length; i++) {
    const page = pages[i];
    const title = await page.title();
    const url = page.url();
    console.log(`Page ${i + 1}: ${title} (${url})`);
  }

  await browser.close();
})();

Finding Pages by URL or Title

You can search for specific pages based on their URL or title:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: false });

  // Create multiple pages
  const page1 = await browser.newPage();
  const page2 = await browser.newPage();

  await page1.goto('https://example.com');
  await page2.goto('https://google.com');

  // Find page by URL
  const pages = await browser.pages();
  const googlePage = pages.find(page => page.url().includes('google.com'));

  if (googlePage) {
    await googlePage.type('input[name="q"]', 'puppeteer tutorial');
    await googlePage.keyboard.press('Enter');
  }

  // Find page by title
  const examplePage = pages.find(async page => {
    const title = await page.title();
    return title.includes('Example');
  });

  await new Promise(resolve => setTimeout(resolve, 3000));
  await browser.close();
})();

Handling Link Clicks That Open New Tabs

When clicking links that open in new tabs (target="_blank"), you need to handle the new page creation:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();

  await page.goto('https://example.com');

  // Listen for new pages (tabs) being created
  const newPagePromise = new Promise(resolve => {
    browser.once('targetcreated', async target => {
      const newPage = await target.page();
      resolve(newPage);
    });
  });

  // Click a link that opens in a new tab
  await page.click('a[target="_blank"]');

  // Wait for the new page to be created
  const newPage = await newPagePromise;

  // Now you can work with the new page
  await newPage.waitForNavigation();
  const title = await newPage.title();
  console.log(`New tab title: ${title}`);

  await browser.close();
})();

Advanced Multi-Tab Scenarios

Parallel Processing Across Multiple Tabs

You can process multiple pages simultaneously for better performance:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: false });

  const urls = [
    'https://example.com',
    'https://google.com',
    'https://github.com',
    'https://stackoverflow.com'
  ];

  // Process all URLs in parallel
  const results = await Promise.all(
    urls.map(async (url) => {
      const page = await browser.newPage();
      await page.goto(url);
      const title = await page.title();
      await page.close(); // Close the page when done
      return { url, title };
    })
  );

  console.log('Results:', results);

  await browser.close();
})();

Managing Tab State and Cleanup

Proper cleanup is essential when working with multiple tabs:

const puppeteer = require('puppeteer');

class TabManager {
  constructor(browser) {
    this.browser = browser;
    this.pages = new Map();
  }

  async createTab(name, url) {
    const page = await this.browser.newPage();
    await page.goto(url);
    this.pages.set(name, page);
    return page;
  }

  async getTab(name) {
    return this.pages.get(name);
  }

  async closeTab(name) {
    const page = this.pages.get(name);
    if (page) {
      await page.close();
      this.pages.delete(name);
    }
  }

  async closeAllTabs() {
    for (const [name, page] of this.pages) {
      await page.close();
    }
    this.pages.clear();
  }

  async listTabs() {
    const tabInfo = [];
    for (const [name, page] of this.pages) {
      const title = await page.title();
      const url = page.url();
      tabInfo.push({ name, title, url });
    }
    return tabInfo;
  }
}

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const tabManager = new TabManager(browser);

  // Create multiple tabs
  await tabManager.createTab('search', 'https://google.com');
  await tabManager.createTab('social', 'https://github.com');
  await tabManager.createTab('docs', 'https://developer.mozilla.org');

  // List all tabs
  const tabs = await tabManager.listTabs();
  console.log('Open tabs:', tabs);

  // Work with specific tab
  const searchTab = await tabManager.getTab('search');
  await searchTab.type('input[name="q"]', 'puppeteer');

  // Close specific tab
  await tabManager.closeTab('docs');

  // Cleanup
  await tabManager.closeAllTabs();
  await browser.close();
})();

Event Handling for Tab Management

Puppeteer provides events to monitor tab creation and closure:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: false });

  // Listen for new tabs
  browser.on('targetcreated', async (target) => {
    if (target.type() === 'page') {
      const page = await target.page();
      console.log(`New tab created: ${page.url()}`);
    }
  });

  // Listen for tab closure
  browser.on('targetdestroyed', (target) => {
    if (target.type() === 'page') {
      console.log(`Tab closed: ${target.url()}`);
    }
  });

  // Create and close tabs
  const page = await browser.newPage();
  await page.goto('https://example.com');

  setTimeout(async () => {
    await page.close();
  }, 2000);

  await new Promise(resolve => setTimeout(resolve, 3000));
  await browser.close();
})();

Working with Multiple Browser Contexts

Browser contexts allow you to create isolated environments, similar to incognito windows:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: false });

  // Create multiple browser contexts
  const context1 = await browser.createIncognitoBrowserContext();
  const context2 = await browser.createIncognitoBrowserContext();

  // Each context has its own cookies and storage
  const page1 = await context1.newPage();
  const page2 = await context2.newPage();

  await page1.goto('https://example.com');
  await page2.goto('https://example.com');

  // Set different cookies for each context
  await page1.setCookie({ name: 'user', value: 'user1', domain: 'example.com' });
  await page2.setCookie({ name: 'user', value: 'user2', domain: 'example.com' });

  // Verify cookies are isolated
  const cookies1 = await page1.cookies();
  const cookies2 = await page2.cookies();

  console.log('Context 1 cookies:', cookies1);
  console.log('Context 2 cookies:', cookies2);

  await context1.close();
  await context2.close();
  await browser.close();
})();

Best Practices for Multi-Tab Management

1. Resource Management

Always close pages when you're done with them to free up memory:

const page = await browser.newPage();
try {
  await page.goto(url);
  // Do your work
  const data = await page.evaluate(() => {
    return document.title;
  });
  return data;
} finally {
  await page.close(); // Always close the page
}

2. Error Handling

Implement proper error handling for each tab:

const results = await Promise.allSettled(
  urls.map(async (url) => {
    const page = await browser.newPage();
    try {
      await page.goto(url, { waitUntil: 'networkidle0' });
      const title = await page.title();
      return { url, title, success: true };
    } catch (error) {
      return { url, error: error.message, success: false };
    } finally {
      await page.close();
    }
  })
);

3. Performance Optimization

Limit the number of concurrent tabs to avoid overwhelming the system:

const puppeteer = require('puppeteer');

async function processBatch(browser, urls, batchSize = 3) {
  const results = [];

  for (let i = 0; i < urls.length; i += batchSize) {
    const batch = urls.slice(i, i + batchSize);
    const batchResults = await Promise.all(
      batch.map(async (url) => {
        const page = await browser.newPage();
        try {
          await page.goto(url);
          const title = await page.title();
          return { url, title };
        } finally {
          await page.close();
        }
      })
    );
    results.push(...batchResults);
  }

  return results;
}

Python Implementation with Pyppeteer

For Python developers, here's how to handle multiple tabs using Pyppeteer:

import asyncio
from pyppeteer import launch

async def handle_multiple_tabs():
    browser = await launch(headless=False)

    # Create multiple pages
    page1 = await browser.newPage()
    page2 = await browser.newPage()
    page3 = await browser.newPage()

    # Navigate to different URLs
    await page1.goto('https://example.com')
    await page2.goto('https://google.com')
    await page3.goto('https://github.com')

    # Get all pages
    pages = await browser.pages()

    # Process each page
    for i, page in enumerate(pages):
        title = await page.title()
        url = page.url
        print(f"Page {i + 1}: {title} ({url})")

    await browser.close()

# Run the function
asyncio.get_event_loop().run_until_complete(handle_multiple_tabs())

Command Line Tools for Multi-Tab Testing

You can also use command-line tools to test multi-tab scenarios:

# Launch Chrome with multiple tabs
google-chrome --new-window "https://example.com" "https://google.com" "https://github.com"

# Use Chrome DevTools Protocol
node -e "
const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com');
  console.log(await page.title());
  await browser.close();
})();
"

Alternative: Using Playwright for Multi-Tab Management

While Puppeteer is excellent for multi-tab scenarios, you might also consider Playwright for multiple browser support, which offers similar functionality with additional features and better performance in some cases.

Troubleshooting Common Issues

Memory Leaks

If you're experiencing memory leaks, ensure you're properly closing pages:

// Always close pages in a finally block
let page;
try {
  page = await browser.newPage();
  await page.goto(url);
  // Process page
} finally {
  if (page) {
    await page.close();
  }
}

Tab Detection Issues

Some websites detect multiple tabs. Use different user agents or contexts:

const page = await browser.newPage();
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');

Conclusion

Managing multiple tabs and windows in Puppeteer requires understanding the Page API and proper resource management. By using the techniques outlined above, you can build robust automation scripts that handle complex multi-tab scenarios efficiently. Remember to always close pages when done, handle errors gracefully, and consider performance implications when working with many concurrent tabs.

Whether you're scraping multiple pages simultaneously, handling user interactions that open new tabs, or building complex automation workflows, Puppeteer's multi-tab capabilities provide the flexibility and control you need for sophisticated web automation tasks.

Try WebScraping.AI for Your Web Scraping Needs

Looking for a powerful web scraping solution? WebScraping.AI provides an LLM-powered API that combines Chromium JavaScript rendering with rotating proxies for reliable data extraction.

Key Features:

  • AI-powered extraction: Ask questions about web pages or extract structured data fields
  • JavaScript rendering: Full Chromium browser support for dynamic content
  • Rotating proxies: Datacenter and residential proxies from multiple countries
  • Easy integration: Simple REST API with SDKs for Python, Ruby, PHP, and more
  • Reliable & scalable: Built for developers who need consistent results

Getting Started:

Get page content with AI analysis:

curl "https://api.webscraping.ai/ai/question?url=https://example.com&question=What is the main topic?&api_key=YOUR_API_KEY"

Extract structured data:

curl "https://api.webscraping.ai/ai/fields?url=https://example.com&fields[title]=Page title&fields[price]=Product price&api_key=YOUR_API_KEY"

Try in request builder

Get Started Now

WebScraping.AI provides rotating proxies, Chromium rendering and built-in HTML parser for web scraping
Icon