ScreenshotNeo

BlogHow-to

How to Select an Option from a Dropdown with Puppeteer

Select native HTML dropdown options with Puppeteer using page.select or locators. See runnable examples for multiple values, iframes, and troubleshooting.

By the ScreenshotNeo team4 October 20266 min read

For a native HTML <select>, use Puppeteer’s page.select() and pass the option’s value, not necessarily its visible label:

const selected = await page.select('select#colors', 'blue');
console.log(selected); // ['blue']

page.select() returns the values it successfully selected and triggers input and change events. It only works with a real <select> element. For an application menu built from buttons or list items, use locators to interact with that widget instead. See the official Page.select API reference and the page interactions guide.

1. Select an option in a native dropdown

Here is a complete Node.js example. It opens a page, selects an option, checks the returned values, and closes the browser. Install Puppeteer in your project with npm install puppeteer, then save this as select-option.js and run node select-option.js.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    await page.setContent(`
      <label for="colors">Color</label>
      <select id="colors">
        <option value="">Choose a color</option>
        <option value="blue">Blue</option>
        <option value="green">Green</option>
      </select>
    `);

    const selected = await page.select('select#colors', 'blue');
    if (selected.length !== 1 || selected[0] !== 'blue') {
      throw new Error(`Expected blue to be selected; got ${JSON.stringify(selected)}`);
    }

    console.log('Selected:', selected);
  } finally {
    await browser.close();
  }
})().catch(error => {
  console.error(error);
  process.exitCode = 1;
});

Use a stable selector owned by the page, such as an ID, name, or test attribute. A positional selector like select:nth-of-type(2) may break when the form structure changes.

Use the option value

Given <option value="blue">Blue</option>, pass 'blue'. The visible label may differ from the value—for example, a translated label or a formatted date. Inspect the rendered option’s value when a selection does not take effect.

Check the result

The resolved value is a string array containing the option values successfully selected. Checking it makes a test fail close to the cause if the selector or expected value changes:

const selected = await page.select('select[name="country"]', 'ca');
if (!selected.includes('ca')) {
  throw new Error(`Country was not selected: ${JSON.stringify(selected)}`);
}

The API triggers the select’s input and change events after the provided options have been selected. That lets ordinary page event handlers respond to the change.

2. Select one or several values

For a single select, pass the desired value. If you pass more than one, only the first value is considered. For a <select multiple>, pass each desired value as another argument; the returned array reports the values selected.

// Single select
const one = await page.select('select#size', 'medium');

// Multiple select
const many = await page.select('select#features', 'storage', 'shipping', 'support');
console.log(many);

Check that the markup really includes the multiple attribute if you expect several selections. Each supplied value must correspond to an option’s actual value.

3. Use a locator for the general interaction workflow

Puppeteer’s interactions guide recommends locators for element interactions. A locator’s fill() method supports select elements and automatically waits for the element to be present and in the right state:

await page.locator('select#colors').fill('blue');

Choose based on what the code needs: use locator.fill() when following the guide’s general locator-first workflow; use page.select() when you want its explicit multi-value arguments and the returned list of selected values. For a locator-based test that needs verification, read the selected value from the page after filling:

await page.locator('select#colors').fill('blue');
const value = await page.locator('select#colors').evaluate(select => select.value);
if (value !== 'blue') throw new Error(`Unexpected value: ${value}`);

4. Select an option inside an iframe

A page-level selector does not search inside a child frame. Find the frame and call its select() method. The Frame API operates on the first matching select in that frame and throws if it cannot find one. See the Frame.select API reference.

const frame = page.frames().find(frame => frame.url().includes('/form'));
if (!frame) throw new Error('Form frame was not found');

const selected = await frame.select('select#colors', 'blue');
if (!selected.includes('blue')) throw new Error('Blue was not selected');

Choose the frame by a stable URL or another property that identifies the intended iframe. If several frames match, make the condition more specific. For an iframe that loads after the main page, wait for the frame to appear before selecting.

5. Handle custom dropdown widgets

Many interfaces that look like dropdowns are not native selects. They may be buttons that open a listbox, a searchable combobox, or a menu rendered elsewhere in the document. page.select() expects a matching <select> and throws for other elements.

Inspect the rendered markup and use locators for the widget’s real controls. For example, a generic button-and-option pattern might look like this, but adapt selectors and accessible names to the actual page:

await page.getByRole('button', { name: 'Choose a color' }).click();
await page.getByRole('option', { name: 'Blue' }).click();

Do not assume that every custom widget exposes an element with role option; inspect its accessibility tree or markup and target the controls the page actually provides.

6. Troubleshooting

Symptom Likely cause Fix
page.select() throws The selector matches no element, or it matches something other than a native <select>. Check the selector and rendered markup. Wait for the control if it is created asynchronously, or use the correct frame context.
The call runs but the expected value is absent The argument is a visible label rather than the option value, or the value is not present among the options. Inspect each option’s value and compare it with the returned string array.
Only one value is selected The target is a single select, which considers only the first supplied value. Use a select with the multiple attribute when the form supports several values.
The select is inside an iframe Page-level operations target the main page context. Find the child frame and call frame.select() on it.
A dropdown-like control is rejected It is a custom widget rather than a native select. Use locators to open it and choose the appropriate item in its actual markup.
The selector is correct but the control is not ready The page has not rendered the control yet. Use a locator, which waits for the element to be present and in the right state, or wait explicitly before using a lower-level API.

7. Reliability, speed, and cost considerations

  • Prefer stable selectors. IDs, names, and application-owned test attributes tend to survive layout changes better than selectors based on element order.
  • Await every interaction. Puppeteer’s selection methods are asynchronous. Awaiting them ensures the selection and its events complete before the next assertion or action.
  • Keep browser cleanup in a finally block. This closes the browser if navigation, selection, or an assertion fails.
  • Use the narrowest context. A page selector is appropriate for the main document; an iframe’s own frame context is required for its contents.
  • Avoid unnecessary setup for a single selection. The selection call itself is direct; most runtime and resource cost comes from launching and keeping a browser open, loading the target page, and any waits your workflow requires.

API details can vary by installed Puppeteer release. If an older project behaves differently, check its installed version and compare with the current official references linked above.

8. Or skip the browser setup

If your goal is to capture a page after automating it, ScreenshotNeo can return a screenshot or PDF with one GET request. It does not perform dropdown interaction; use Puppeteer when the page must be changed before capture.

See the ScreenshotNeo documentation for request options. Example:

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

ScreenshotNeo accepts cookie and consent banners like a visitor and removes 60+ known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify 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. Every feature is on every plan.

Sign up for 1,000 free screenshots a month, with no card required.

9. FAQ

Does Puppeteer select by the option’s label?

Pass the option’s value. If label and value are different, use the value from the option markup.

What events fire after selection?

The select operation triggers input and change events after the provided options have been selected.

Can I select multiple values on a single select?

No. A single select considers only the first supplied value. Several values apply to a select marked multiple.

Where can I check the method signature?

Use the official Page.select, Frame.select, and page interactions documentation.