How to Filter Puppeteer Targets
Find the Puppeteer page, popup, or worker you need by filtering active targets, waiting for a match, or tracking target events.
To filter targets that already exist, get a snapshot with browser.targets() or context.targets(), then use JavaScript’s filter() or find() with target.type() and target.url(). To wait for a target that will appear, use browser.waitForTarget(predicate) or context.waitForTarget(predicate). Use target.page() for page-like targets and target.worker() for service or shared workers.
A target is a browser object such as a page or worker. Filtering targets is useful when a flow opens a popup, when you need an extension’s service worker, or when a browser has multiple contexts and you need to select the right target. The examples below use Puppeteer’s JavaScript API. Check the API documentation for the version installed in your project, since the documentation versions can differ. Browser.targets(), BrowserContext.targets()
Choose between a snapshot, a wait, and lifecycle events
| Need | Use | What it does |
|---|---|---|
| Find targets that exist now, across browser contexts | browser.targets() |
Returns the active targets across all contexts. |
| Find targets in one context | context.targets() |
Returns active targets in that context. |
| Wait for a popup, page, or worker to appear | waitForTarget(predicate) |
Resolves with the first target matching the predicate. |
| React to targets being created, changing URL, or closing | Context target events | Lets the program maintain a live view instead of taking one snapshot. |
browser.targets() is browser-wide; context.targets() scopes enumeration to one context. Note that the documented BrowserContext.waitForTarget() method may look across all open browser contexts, so use a context-scoped snapshot when strict context scoping is important. Browser.targets() · BrowserContext API
Run a complete target-filtering example
This Node.js script launches Chromium, opens a page, filters a current snapshot for page targets, and prints matches for a URL prefix. Save it as filter-targets.mjs, install Puppeteer in the project with npm install puppeteer, then run node filter-targets.mjs.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
const prefix = 'https://example.com/';
const matches = browser.targets().filter(target =>
target.type() === 'page' && target.url().startsWith(prefix)
);
for (const target of matches) {
console.log({ type: target.type(), url: target.url() });
const targetPage = await target.page();
if (targetPage) {
console.log('Title:', await targetPage.title());
}
}
} finally {
await browser.close();
}
filter() returns every match; use find() if you want only the first one. Matching a type and URL together helps avoid selecting unrelated targets of the same kind. URL checks are ordinary string comparisons: use startsWith(), endsWith(), or includes() according to your route structure. If query parameter order or URL normalization matters, parse the URL and compare its components instead of comparing the raw string.
Filter a current snapshot
Find a page by URL
const appTarget = browser.targets().find(target =>
target.type() === 'page' && target.url().startsWith('https://app.example/')
);
if (!appTarget) {
throw new Error('No active app page target found');
}
const appPage = await appTarget.page();
if (!appPage) {
throw new Error('Matched target did not provide a Page');
}
Limit the snapshot to one context
const context = browser.defaultBrowserContext();
const workers = context.targets().filter(target =>
target.type() === 'service_worker'
);
for (const target of workers) {
console.log(target.url());
}
Use browser-wide enumeration when targets across contexts are relevant. Use a context snapshot when isolation matters, for example when the same app is open in multiple contexts. A snapshot only describes active targets at the moment it is taken: a target created after that call will not appear in its result.
Wait for a target that will appear
Register the wait before triggering the action that creates the target. Otherwise, a fast popup or worker can appear before the wait begins. Make the predicate specific enough to distinguish the intended target from other pages or workers.
Wait for a popup
const popupPromise = browser.waitForTarget(target =>
target.type() === 'page' && target.url().endsWith('/popup.html')
);
await page.evaluate(() => window.open('/popup.html', '_blank'));
const popupTarget = await popupPromise;
const popupPage = await popupTarget.page();
if (!popupPage) {
throw new Error('Popup target did not provide a Page');
}
When the popup is opened by a page, Puppeteer also documents waiting through its browser context. Note that the context method’s reference says it looks across all open contexts; if that scope is too broad, include a distinctive URL and type in the predicate. BrowserContext.waitForTarget()
Wait for an extension service worker
const workerTarget = await browser.waitForTarget(target =>
target.type() === 'service_worker' && target.url().endsWith('background.js')
);
const worker = await workerTarget.worker();
if (!worker) {
throw new Error('Matched target did not provide a WebWorker');
}
console.log('Service worker target:', workerTarget.url());
The suffix predicate follows Puppeteer’s Chrome Extensions guide pattern. It assumes the extension has one relevant service worker with that URL suffix; if there may be several, add a stronger URL condition. Puppeteer Chrome Extensions guide
Know the target types and conversion methods
The documented target type strings are page, service_worker, shared_worker, background_page, browser, other, and webview. Filter by type before converting the target to a page or worker. TargetType enum
| Target kind | Typical handling |
|---|---|
page |
await target.page() |
background_page or webview |
await target.page() can return a Page for these types. |
service_worker or shared_worker |
await target.worker() |
other |
Use await target.asPage() only when forcefully treating this target as a page is intentional. |
browser |
It is the browser target, not an ordinary tab to handle as a page. |
page() returns null for target kinds other than page, webview, or background page. worker() returns null for kinds other than service worker or shared worker. Check for null even after filtering: it keeps the code safe if the target’s kind or state is not what the program expected. asPage() forcefully creates a page for a target of any type and is intended for special cases such as a target of type other. Target API
Track target lifecycle changes
Use context events if the program needs to react continuously as targets appear, change URL, or close. The target-changed event fires when a target URL changes. Attach listeners before the work that creates the targets, and remove them when tracking ends.
function onCreated(target) {
console.log('created:', target.type(), target.url());
}
function onChanged(target) {
console.log('changed:', target.type(), target.url());
}
function onDestroyed(target) {
console.log('destroyed:', target.type(), target.url());
}
context.on('targetcreated', onCreated);
context.on('targetchanged', onChanged);
context.on('targetdestroyed', onDestroyed);
try {
// Run the interaction that creates or navigates targets here.
} finally {
context.off('targetcreated', onCreated);
context.off('targetchanged', onChanged);
context.off('targetdestroyed', onDestroyed);
}
Use the event stream for ongoing tracking and a snapshot for a one-time lookup. If both are needed, subscribe first and then take the snapshot so a target appearing around the transition is less likely to be missed. The event reference documents these lifecycle events. BrowserContext events
Troubleshooting common target-filtering problems
| Symptom | Likely cause | Fix |
|---|---|---|
| The snapshot has no match | The target has not opened yet, navigated to the expected URL, or has already closed. | Use waitForTarget() for a future target; inspect the actual type() and url() values in the snapshot. |
| The wait never resolves | The action was not triggered, the predicate is too strict, or the target URL does not match the assumed suffix or prefix. | Start the wait before the action, log created and changed targets, then adjust the predicate to the observed URL and type. |
target.page() returns null |
The target is a worker or another non-page-like type. | Check type(); use worker() for service/shared workers and handle its nullable result. |
target.worker() returns null |
The target is not a service worker or shared worker. | Check the type first, and handle null rather than assuming conversion succeeded. |
| The wrong tab is selected | Multiple targets share a type or broad URL substring. | Match both type and a more distinctive URL condition; consider matching origin and pathname separately. |
| A context lookup sees an unexpected target | The wait method can search all open contexts, unlike a context-scoped target snapshot. | Use context.targets() for a scoped snapshot, or tighten the wait predicate using URL and type. |
| A target is missed during ongoing work | A snapshot was taken before creation, or listeners were added after the event. | Subscribe before triggering the action; use lifecycle events for continuous monitoring. |
Performance, reliability, and cost
- Performance: target filtering operates on the array returned by Puppeteer. Use one snapshot and a focused predicate when possible; repeated snapshots inside a tight loop add unnecessary work and can make logs noisy.
- Reliability: predicates should describe the target’s identity using type plus URL characteristics. Treat matching as potentially ambiguous unless the predicate is unique for your app, and handle nullable conversion results.
- Timing: waits are appropriate for future targets, snapshots for currently active ones, and events for ongoing lifecycle needs. Start waits and listeners before the action that can cause the target to appear.
- Cost: Puppeteer itself is an open-source browser automation library; runtime cost depends on the browser and infrastructure you run. This target-filtering task does not require a screenshot API.
Or skip the browser setup
If your goal is to get a screenshot rather than inspect Puppeteer’s target objects, ScreenshotNeo provides a website screenshot API. One GET request returns a PNG, JPEG, WebP, or PDF, with options such as full-page capture, selector capture, custom wait conditions, and device presets. See the ScreenshotNeo API docs.
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)
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 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 cost nothing, and responses identify page verdict and billing in headers. Its MCP server gives AI agents screenshot, page-info, and PDF tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.
Create a free ScreenshotNeo account for 1,000 screenshots a month with no card.
FAQ
Does browser.targets() include targets from incognito contexts?
It returns active targets across browser contexts. Use a context’s targets() method when you want a context-specific snapshot.
Can I use browser.pages() instead?
Use target enumeration when you need workers or other target kinds as well as pages. The context API notes that pages() does not list non-visible pages such as background pages.
Should I use asPage() for every target?
No. Use page() or worker() according to the target kind. Reserve asPage() for cases where forcefully treating a target as a page is intended.


