How to List Pages in a Puppeteer BrowserContext
Use `await context.pages()` to list the open pages in one Puppeteer BrowserContext. Learn what it includes, how it differs from `browser.pages()`, and how to handle background pages.
Call await context.pages(). It returns a Promise<Page[]> containing the open, visible pages associated with that BrowserContext.
const pages = await context.pages();
for (const page of pages) {
console.log(await page.title(), page.url());
}
Use this when you want pages in one context, such as an isolated context with its own cookies and local storage. Use browser.pages() when you need pages across the browser’s contexts. Puppeteer’s official BrowserContext API reference documents the method and its optional includeAll argument.
List pages in a BrowserContext
Get the context, await its page list, then use the returned Page objects as usual:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const context = await browser.createBrowserContext();
const firstPage = await context.newPage();
await firstPage.goto('https://example.com');
const pages = await context.pages();
console.log(`Found ${pages.length} page(s)`);
for (const page of pages) {
console.log({ title: await page.title(), url: page.url() });
}
} finally {
await browser.close();
}
context.pages() does not create pages. It reports pages that are already open when the call runs. Await the promise before iterating or calling page methods.
Choose the right scope
| Call | Scope | Use it when |
|---|---|---|
context.pages() |
One BrowserContext |
You need pages associated with a specific context. |
browser.pages() |
Across browser contexts | You need a browser-wide list. |
browser.browserContexts() |
The browser’s contexts | You need to inspect which contexts exist, then enumerate each context’s pages. |
A BrowserContext represents an isolated user context, including storage such as cookies and local storage. A popup opened by a page belongs to that page’s context. If you are working with an isolated context, context.pages() keeps your enumeration scoped to it. See the official references for Browser.pages() and Browser.browserContexts().
Background pages and the includeAll option
By default, context.pages() omits non-visible pages, such as a background page. The method signature has an optional includeAll argument, but Puppeteer documents it as experimental. Check the documentation and behavior for the Puppeteer version your application uses before depending on it:
// Experimental: verify support and behavior for your Puppeteer version.
const pages = await context.pages(true);
For non-visible pages, Puppeteer documents Target.page() as a way to obtain a page from a target. You can inspect active targets with context.targets():
const targets = context.targets();
for (const target of targets) {
console.log(target.type(), target.url());
if (target.type() === 'page') {
const page = await target.page();
if (page) {
console.log(await page.title(), page.url());
}
}
}
Targets are not the same thing as visible pages: a context can have targets for things other than ordinary tabs. Check the target type before treating it as a page. See Puppeteer’s BrowserContext.targets() reference and Target.page() reference.
Handle pages that open later
The list is a snapshot of open pages at the time you call the method. If an action opens a popup, wait for that event and then enumerate again:
const popupPromise = new Promise(resolve => {
browser.once('targetcreated', async target => {
if (target.type() === 'page') {
resolve(await target.page());
}
});
});
await page.click('a[target="_blank"]');
const popup = await popupPromise;
const pagesAfterPopup = await context.pages();
console.log(pagesAfterPopup.map(p => p.url()));
console.log('Popup URL:', popup?.url());
Attach the listener before the click so a fast popup is not missed. If your code already knows which context owns the opener, use that context’s page list after the popup event to confirm the resulting pages.
Common errors and fixes
| Symptom | Cause | Fix |
|---|---|---|
context.pages is not a function |
The value is not a Puppeteer BrowserContext, or a different object was assigned to context. |
Check where the context comes from. For a new isolated context, use await browser.createBrowserContext(); for the default context, use browser.defaultBrowserContext(). |
pages is a promise or iteration fails |
The call was not awaited. | Use const pages = await context.pages() before iterating. |
| An expected page is missing | It may be non-visible, opened in another context, or created after the list was obtained. | Check the correct context, call again after creation, or inspect context.targets() for active targets. |
| A page method fails after enumeration | The page may have closed between listing and use. | Check page.isClosed() before work that can race with page closure, and handle closure as a normal lifecycle event. |
| Popup is missing immediately after a click | Popup creation is asynchronous, or the event listener was attached too late. | Register the target or popup listener before triggering the click, await the event, then enumerate again. |
Performance and reliability
Page enumeration is generally a small browser-control operation, but avoid calling it repeatedly in a tight polling loop. Take a fresh snapshot when you need one, and use browser or target events when you need to react to pages opening and closing. Treat page lists as short-lived: tabs can close or navigate after enumeration.
Keep the context reference that created or owns the work. This reduces accidental cross-context operations and makes tests more reliable when multiple isolated sessions run in one browser. Close contexts or the browser in a finally block so a failure during page inspection does not leave browser processes running.
Or skip the browser setup
If your goal is to capture a URL rather than inspect pages in a live Puppeteer session, ScreenshotNeo is a website screenshot API and MCP server. A single GET request returns a PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation for parameters and response details.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
ScreenshotNeo accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for 1,000 free screenshots a month, with no card required.
FAQ
Does context.pages() return a promise?
Yes. Its documented return type is Promise<Page[]>, so use await.
Does it include pages from other contexts?
No. It lists pages in the context on which you call it. Use browser.pages() for the browser-wide list.
Does it include background pages?
Not by default. Non-visible pages are omitted; the optional includeAll argument is experimental, so verify the behavior for your installed Puppeteer version.


