How to capture a Puppeteer screenshot after selecting a dropdown option
Select a native dropdown with Puppeteer, wait for the page to update, then capture the viewport, full page, or a specific element.
For a native HTML <select>, call await page.select(selector, value), wait for any page update that matters to the screenshot, then call await page.screenshot(...). Puppeteer’s page.select() selects by option value and triggers input and change events. A custom dropdown is different: interact with its actual buttons or menu items.
1. Install Puppeteer and capture after selecting an option
This runnable example uses Puppeteer 25.12.0. It opens a page, selects the option whose value is CA, waits for an application result, and saves a PNG. Replace the example URL and selectors with those from your page.
npm install puppeteer
// screenshot.mjs
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.select('select#country', 'CA');
// Replace this with a condition that represents the finished state you need.
await page.waitForSelector('#result', { visible: true });
await page.screenshot({ path: 'selected.png' });
} finally {
await browser.close();
}
Run it with node screenshot.mjs. For the API details, see Puppeteer’s Page class, Page interactions guide, and Screenshots guide.
2. Select the right value and wait for the result
The second argument to page.select() is the option’s value, not necessarily its visible label. For example, in <option value="CA">Canada</option>, pass 'CA'. The method resolves after selecting the matching option or options and dispatching the selection events. Await it before taking the screenshot.
Selection completing does not mean the site has finished an asynchronous fetch, animation, or framework render triggered by the change. Wait for a visible outcome that matches the state you intend to capture. If selecting the country updates a label, for example:
await page.select('select#country', 'CA');
await page.waitForFunction(() =>
document.querySelector('#selected-country')?.textContent?.trim() === 'Canada'
);
await page.screenshot({ path: 'canada.png' });
Use a condition specific to the page. Puppeteer’s waitForSelector can wait for an element to appear and optionally be visible; its documented default timeout is 30 seconds. waitForFunction is useful when the condition is a particular text, attribute, or application state.
A fixed delay such as await new Promise(resolve => setTimeout(resolve, 1000)) can be useful for a known animation, but it is usually less reliable: it may be longer than needed or finish before a slow update. Prefer waiting for the resulting state.
3. Handle a custom dropdown
page.select() is for native <select> elements. A custom dropdown often uses a button, list, and clickable options instead. Inspect its markup and use locators to interact with those controls. Puppeteer recommends locators for selecting and interacting with elements; locators perform precondition checks as part of the interaction.
// Example pattern only: use the accessible names or selectors on your page.
await page.locator('button[aria-haspopup="listbox"]').click();
await page.locator('[role="option"]').filter(option =>
option.getAttribute('data-value') === 'CA'
).click();
await page.waitForFunction(() =>
document.querySelector('#selected-country')?.textContent?.trim() === 'Canada'
);
await page.screenshot({ path: 'custom-dropdown.png' });
Adapt the locator to the widget’s real markup and accessible names. The pattern above is illustrative; pages do not share a standard custom-dropdown structure. See the Puppeteer interaction guide for locator usage.
4. Choose the screenshot area and format
By default, page.screenshot() captures the current viewport as PNG. Select the scope that communicates the result you need:
| Need | Example |
|---|---|
| Current viewport | await page.screenshot({ path: 'selected.png' }) |
| Entire document | await page.screenshot({ path: 'selected-full.png', fullPage: true }) |
| A rectangular region | await page.screenshot({ path: 'selected-area.png', clip: { x: 0, y: 0, width: 800, height: 500 } }) |
| One element | Use element.screenshot({ path: 'result.png' }) on an element handle. |
For example, to save the result panel after the selection:
const result = await page.$('#result');
if (!result) throw new Error('Result panel was not found');
await result.screenshot({ path: 'result.png' });
An element screenshot scrolls the element into view if needed. Review Puppeteer’s ScreenshotOptions and screenshot guide for supported settings and behavior.
5. Troubleshoot common failures
| Symptom | Likely cause | Fix |
|---|---|---|
page.select() finds no matching option |
The selector is wrong, the element is not a native select, or the passed value differs from the option’s value. | Inspect the page’s HTML and confirm the selector and exact <option value>. Use locators and click the real controls for a custom menu. |
| The screenshot shows the old result | The app updates asynchronously after the selection event. | Wait for the updated text, result element, or another observable condition before capturing. |
| A wait times out | The expected state never appeared, the condition is too strict, or the page has not reached the relevant stage. | Check the selector and expected value; wait for the correct element or state. Increase the timeout only when the operation legitimately needs more time. |
| The menu is open in the image | A custom dropdown was opened, but the option click or close/update step did not complete. | Await the option interaction and wait for the selected state or menu closure before taking the screenshot. |
| The image is cropped or too tall | The default captures only the viewport, or full-page capture includes more content than intended. | Choose viewport, fullPage, clip, or an element screenshot to match the desired output. |
| The browser process remains open after an error | Cleanup was skipped when an operation threw. | Put browser.close() in a finally block, as in the main example. |
6. Reliability, performance, and cost considerations
- Reliability: Base the wait on the actual result, not on selection alone. Keep selectors tied to stable IDs, labels, or accessible roles where possible, and close the browser in a
finallyblock so failures do not leave a process running. - Performance: Wait for the smallest meaningful condition instead of an unnecessarily long fixed sleep or unrelated network activity. Capture only the viewport or element needed when a full-page image is not required.
- Repeatability: If layout affects your output, set the viewport before navigation and keep the same page state and capture options across runs. Test the chosen wait against both fast and slow page updates.
- Cost: Puppeteer is an open-source browser automation library. Your direct operating costs depend on where and how you run the browser, including compute and any browser hosting; this guide makes no cost or speed benchmark claims.
7. Or skip the browser setup
If you only need the finished page as an image, ScreenshotNeo provides a website screenshot API and MCP server. Its API captures a URL in one GET request; the example uses the Stripe homepage. See the ScreenshotNeo API docs for setup and options.
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}`);
Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; response headers report the page verdict and billing status. Its MCP server includes take_screenshot, get_page_info, and capture_pdf tools for AI agents. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Every feature is on every plan. These URL-based captures do not perform the dropdown interaction in your Puppeteer script; use Puppeteer when you need to select a particular option or reproduce custom browser interactions.
Sign up for ScreenshotNeo’s free plan: 1,000 screenshots a month, no card required.
8. FAQ
Can I pass the visible dropdown label to page.select()?
Pass the option’s value. The label and value may match, but they need not; inspect the option markup.
Does selecting an option automatically wait for the page’s data request?
No. It selects and dispatches the relevant events. Wait separately for the page state your capture depends on.
Can ScreenshotNeo select a dropdown before taking a screenshot?
The one-call API captures a URL. For a specific interaction such as choosing an option, use browser automation such as Puppeteer first.


