Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

With puppeteer-cluster, handle multiple tabs by queueing separate jobs and setting maxConcurrency. Each task callback receives one Puppeteer Page—one tab—not an array of tabs. The cluster schedules queued jobs across workers, while your selected concurrency mode determines how each job shares browser state and how isolated it is from other jobs.

For multiple independent URLs, register a task, queue each URL, wait for cluster.idle(), then close the cluster. Choose CONCURRENCY_CONTEXT when jobs should not share page data, CONCURRENCY_PAGE when shared cookies and local storage are intentional, or CONCURRENCY_BROWSER when you want browser-level crash isolation. The project documents CONCURRENCY_CONTEXT as the default and recommends explicitly choosing a mode.

What “multiple tabs” means in puppeteer-cluster

The cluster API’s unit of work is a queued job. When a task runs, its callback receives a page object for interacting with one Chromium tab, along with the job’s data. Queue several jobs to process several pages; do not expect one task callback to receive multiple tabs automatically.

This design separates your work into two decisions: what one job does with its Page, and how many jobs the cluster may run concurrently. For example, a task might navigate to a URL and extract information, run a test, or capture a page. The queue supplies the URL (or other job data), and the cluster supplies the Page for that job.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A cluster manages a pool of Puppeteer workers, tracks jobs and errors, can retry failed work, and can restart a browser after a crash. Its concurrency setting determines the resource arrangement for those jobs; it does not change the fact that each task invocation works with one Page.

Choose a concurrency mode

The built-in modes chiefly differ in whether jobs share browser state and how much isolation they receive. Pick based on the state and failure boundaries your workload requires, rather than assuming one mode is universally best.

Mode Resource per job State between jobs Isolation and suitable use
CONCURRENCY_PAGE A Page for each URL Cookies, local storage, and other state are shared. Less isolated. Use when sharing browser state between jobs is deliberate.
CONCURRENCY_CONTEXT An incognito page/context for each URL Jobs share no data. Use when jobs should keep their page data separate while using the cluster’s browser model.
CONCURRENCY_BROWSER A browser with an incognito page per URL Jobs share no data. Provides browser-level crash isolation: a browser crash for one job does not affect other jobs, according to the project documentation.

Use CONCURRENCY_PAGE for intentional shared state

Cookies and local storage can carry state from one job to another in this mode. That can be useful when jobs are meant to operate in the same browser state, but it is a poor fit for unrelated users, accounts, or tests whose results depend on starting independently. A prior job’s state may affect a later job, so make that sharing part of the design rather than an accidental side effect.

Use CONCURRENCY_CONTEXT for isolated job data

This mode gives each URL an incognito page/context and shares no data between jobs. It is the documented default, but the project recommends specifying the desired mode explicitly. Explicit configuration makes the sharing boundary visible in code and helps prevent a future reader from mistaking the default for a deliberate requirement.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use CONCURRENCY_BROWSER when browser crashes need containment

This mode uses a browser with an incognito page per URL. The project describes its key isolation property as containing a browser crash for one job so it does not affect other jobs. Choose it when that failure boundary matters; the documentation supplied here does not establish a speed, memory, or throughput advantage over the other modes.

Queue several pages: a complete example

This example runs separate URL jobs using isolated contexts. It follows the cluster’s launch, task, queue, idle, and close pattern. The URLs are illustrative; replace them with pages you are authorized to access.

const { Cluster } = require('puppeteer-cluster');

(async () => {
  const cluster = await Cluster.launch({
    concurrency: Cluster.CONCURRENCY_CONTEXT,
    maxConcurrency: 2,
  });

  await cluster.task(async ({ page, data: url }) => {
    await page.goto(url);
    // Extract, test, or capture this page.
    console.log(`Finished: ${url}`);
  });

  cluster.queue('https://example.com/one');
  cluster.queue('https://example.com/two');
  cluster.queue('https://example.com/three');

  await cluster.idle();
  await cluster.close();
})();

What each part does

  • Cluster.launch() starts the cluster with the selected concurrency mode and concurrency limit.
  • cluster.task() registers the function the cluster runs for a job. The callback destructures a single page and that job’s data, here named url.
  • cluster.queue(url) adds a separate job. Each call above contributes one URL job.
  • cluster.idle() waits until queued work has finished.
  • cluster.close() closes the cluster after the queued jobs are complete.

The example sets maxConcurrency: 2, the same value used in the project’s documented example. It is a configuration example, not a benchmark or a promise that two jobs will be optimal for your site or machine.

Set concurrency to match the workload

maxConcurrency limits how many jobs the cluster runs concurrently. Its documented default is 1, so setting the value explicitly is important when you intend to process several jobs at once. A larger limit permits more simultaneous work, but the supplied project documentation does not publish a universal throughput, memory, or speed result for any value.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Begin with a conservative value and compare behavior using your own pages, tasks, and runtime environment. Consider whether pages are heavy, whether the destination tolerates concurrent visits, and whether jobs need separate state. Raise the limit only after checking completion, errors, and resource use under the workload you actually expect. More concurrency is a scheduling choice, not proof of a proportional speed-up.

How to choose a starting mode

  1. List what each job represents: an unrelated page, a page in a shared session, or a job requiring browser-level crash containment.
  2. Choose CONCURRENCY_PAGE only if sharing cookies, local storage, and other state is intended.
  3. Choose CONCURRENCY_CONTEXT when jobs should not share data and you do not specifically require the documented browser-crash boundary of the browser mode.
  4. Choose CONCURRENCY_BROWSER when containing a browser crash between jobs is a requirement.
  5. Set maxConcurrency explicitly, then test the workload and adjust based on observed results rather than an assumed performance multiplier.

State sharing and job design

The mode affects the boundary between jobs, not just the number of tabs. Project tests demonstrate cookie sharing under CONCURRENCY_PAGE and no cookie sharing under CONCURRENCY_CONTEXT or CONCURRENCY_BROWSER. Treat this as a practical correctness concern: shared state can make results order-dependent, while isolated contexts are more appropriate when one job must not inherit another’s browser data.

Keep the task focused on a single job’s input and output. For a URL-processing queue, pass the URL as job data and use the callback’s Page to perform that page’s work. If several actions must happen within the same page, perform those actions inside that job’s callback; if several pages must be processed independently, queue separate jobs.

Do not use a shared-state mode merely to imitate multiple independent users. Conversely, do not assume the isolated modes preserve cookies from a preceding job. Pick the mode that matches the intended session model and validate it with the state your task relies on.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Waiting, errors, and cleanup

Call cluster.idle() before closing when you need all queued work to finish. Closing immediately after adding jobs would not express the documented “wait for queued jobs, then close” sequence. Keep cleanup in the control flow that owns the cluster so successful processing has a clear end point.

The project describes tracking jobs and errors, retrying failed work, and restarting a browser after a crash as cluster capabilities. These behaviors can help manage failures, but they do not make every navigation or page task succeed. The code above intentionally leaves task-specific error policy to the application: decide what constitutes a failed job, what information to log, and whether retrying that operation is safe for its side effects.

Performance, reliability, and cost considerations

The project material describes how concurrency modes isolate jobs, but does not provide comparative throughput, memory, or speed benchmarks. There is therefore no evidence-based universal number of tabs to recommend. A useful limit depends on the pages, work performed, machine, and target site; measure those conditions directly.

Concurrency can let independent jobs overlap, but increasing it also means more work is active at once. Monitor whether jobs complete reliably and whether failures rise as you increase the limit. For stateful workflows, correctness and isolation may matter more than processing as many pages simultaneously as possible. For failure-sensitive workloads, the browser-level isolation documented for CONCURRENCY_BROWSER may be more important than a theoretical speed comparison.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Common problems and fixes

Only one job appears to run at a time

Check whether maxConcurrency is omitted: its documented default is 1. Set it explicitly to the number of simultaneous jobs you want to allow, then confirm that the queued jobs are separate calls to cluster.queue().

A task expects several Page objects

Restructure the work. A task callback receives a Page for one job. Queue one job per page or URL rather than expecting a callback argument containing multiple tabs.

One job sees another job’s cookies or local storage

Check the selected mode. CONCURRENCY_PAGE shares cookies and local storage between jobs. Choose CONCURRENCY_CONTEXT or CONCURRENCY_BROWSER if jobs should share no data, and ensure the task does not rely on state from a previous job.

A later job lacks state set by an earlier job

That is consistent with the no-shared-data behavior of CONCURRENCY_CONTEXT and CONCURRENCY_BROWSER. If the workflow truly requires shared browser state, use CONCURRENCY_PAGE and account for the resulting sharing between jobs.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The process exits before queued work is finished

Use the documented lifecycle order: queue work, await cluster.idle(), then call cluster.close(). This makes completion a prerequisite for cleanup.

A browser crash affects work you expected to be isolated

Review the mode’s documented failure boundary. The project specifically describes CONCURRENCY_BROWSER as isolating a browser crash for one job from other jobs. The other modes have different resource arrangements; do not assume that they provide the same browser-level crash containment.

Or skip the browser setup

If you need a screenshot rather than custom browser automation, ScreenshotNeo offers a one-request screenshot API. It accepts a URL and returns an image or PDF; its documentation is at ScreenshotNeo API docs.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp

ScreenshotNeo removes cookie/consent banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are not billed; each response reports the page verdict and billing status in headers. Its MCP server lets AI agents take screenshots, and the free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Sign up for free and get 1,000 screenshots a month with no card.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Does increasing maxConcurrency guarantee a faster run?

No. The project documentation does not establish a fixed performance gain; compare settings using your own pages and workload.

Can I use puppeteer-cluster for tasks other than screenshots?

Yes. A task callback receives a Puppeteer Page for browser work; the queued job can perform extraction, testing, or capture.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.