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

For ordinary form fields, use Puppeteer’s locator API: await page.locator('input[name="email"]').fill('user@example.com'); It is the high-level way to fill text inputs, textareas, select controls, and contenteditable elements. Use page.type() when the page needs keyboard events for each character, and reserve direct DOM assignment for custom cases where you manage the page’s event behavior yourself.

Use locator.fill() for ordinary form fields

Choose a selector for the intended control, then call fill() with its value:

await page.locator('#username').fill('alice');
await page.locator('textarea[name="message"]').fill('Hello');
await page.locator('select[name="country"]').fill('US');

The method fills the input identified by the locator. Puppeteer determines the control type at runtime, so the same API can handle supported input, textarea, select, and contenteditable controls. For a checkbox, radio button, or switch, pass a boolean instead of text.

For a select control, provide the option value the page expects, as in 'US' above; do not assume that a visible label and an option’s value are identical. If a page uses a custom widget rather than a native supported control, inspect its behavior and use the interaction its interface requires.

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

Choose a selector that identifies the right control

A locator action needs to target the intended field. Prefer a stable id, name, label, role, or accessible name over a broad selector that could match several controls.

await page.locator('#search').fill('Puppeteer');
await page.locator('input[name="email"]').fill('user@example.com');
await page.locator('::-p-aria(Search)').fill('Puppeteer');

Puppeteer supports CSS selectors as well as selector forms such as ARIA, text, and XPath. The accessible-name selector above targets a control exposed with the name “Search.” It can be more resilient than depending on a page’s incidental DOM layout, provided the page exposes an appropriate accessible name.

A selector such as input is fine only when there is exactly one relevant match. On a form with multiple inputs, narrow it by id, name, or another stable property; otherwise the locator may not identify the field you intended.

Understand locator waiting and timeouts

Locators do more than find an element. Before acting, they wait for it to be in the viewport, visible, enabled, and stable across two animation frames. If those conditions are not met, the action can retry until its timeout. Locator timeouts inherit the page timeout, and a per-locator timeout can be set when one interaction needs a different limit.

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

This automatic waiting is useful for fields that appear after navigation or a page update. It does not make every form ready: a field can be visible and enabled before application data has finished loading, or an application can require another condition before accepting input. If a fill times out, check the selector and the field’s actual state rather than immediately increasing the timeout.

Use page.type() when keyboard events matter

fill() is the normal choice for setting a value. Use page.type() when the site responds to keyboard entry itself—for example, when its behavior depends on a per-character handler.

await page.type('#username', 'alice');
await page.type('#username', ' slowly', {delay: 75});

page.type(selector, text) sends keyboard events for each character, including keydown, keypress/input, and keyup. Its delay option is the time between key presses in milliseconds and defaults to zero. Use a delay only when the interaction needs it; it makes entry take longer and is not a general reliability fix.

Unlike a locator action, the examples above use the Page API directly. If you need locator waiting and keyboard-style entry, first ensure the target is ready and then use the keyboard interaction pattern appropriate to your page. Choose the API based on the page’s event requirements, not just on whether the end value looks correct.

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.

Use page evaluation only for custom DOM operations

Directly assigning element.value is a lower-level option, not the default replacement for fill(). It can be useful when you need a custom DOM operation, but a site may rely on framework state, custom setters, or event listeners. A value that appears in the DOM is not necessarily a value the application has processed.

await page.evaluate(({selector, value}) => {
  const element = document.querySelector(selector);
  if (!(element instanceof HTMLInputElement)) {
    throw new Error('Expected an input element');
  }
  element.value = value;
  element.dispatchEvent(new Event('input', {bubbles: true}));
  element.dispatchEvent(new Event('change', {bubbles: true}));
}, {selector: '#username', value: 'alice'});

This example checks that the selector found an HTMLInputElement, assigns the value, and dispatches bubbling input and change events. That makes the operation more deliberate than a silent property assignment, but it does not guarantee compatibility with every framework or custom control. If the application still does not respond, use its expected interaction pattern; keyboard-driven entry through page.type() is the documented choice when keyboard events matter.

Read a value back with $eval()

Use $eval() for a one-element operation such as checking the field’s current value:

const value = await page.$eval(
  '#username',
  (element) => (element instanceof HTMLInputElement ? element.value : '')
);

$eval() passes the first matching element to the page function and throws if nothing matches. The type check makes the example return an empty string for a non-input element; change the check if you are reading a textarea or another control. In TypeScript, annotate the callback parameter as HTMLInputElement when needed.

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

Complete Puppeteer example: fill and submit a form

This Node.js example launches a browser, opens the form, fills an email input, submits it, and closes the browser even if navigation or interaction fails:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.goto('https://example.com/form');
  await page.locator('input[name="email"]').fill('user@example.com');
  await page.locator('button[type="submit"]').click();
} finally {
  await browser.close();
}

Save this as an ES module in a project where Puppeteer is installed, replacing the example URL and field selector with those for your form. The try/finally block ensures the browser is closed after the work; without cleanup, a failed assertion or timed-out action can leave a browser process running. If the submit button triggers navigation and the next step depends on the new page, wait for that result explicitly rather than assuming the click means the destination is ready.

When lower-level selector waiting is appropriate

If a locator does not cover a custom action, Puppeteer also provides lower-level APIs such as waitForSelector() and an ElementHandle:

const input = await page.waitForSelector('#username');
if (!input) throw new Error('Input not found');
await input.click();
await input.dispose();

This illustrates the availability and cleanup of an element handle; for normal value entry, prefer the locator examples above. waitForSelector() waits for DOM availability only. It does not automatically retry a subsequent action if that action fails, and the returned handle should be disposed when you are done with it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value
The SQL Programming Language: .
  • Used Book in Good Condition
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshoot common input failures

  • The fill times out. Confirm the selector matches the intended element and that it becomes visible, enabled, and stable. If the field is inserted later, wait for the relevant page state; extending a timeout will not fix a selector that never matches.
  • The wrong field changes. Replace a broad selector such as input with an id, name, label, or accessible-name selector that identifies the intended control.
  • The value is present but the form ignores it. The site may depend on framework state or input handlers. Prefer fill() for ordinary controls, or page.type() when the site needs keyboard events. Treat direct assignment and manually dispatched events as a custom fallback, not a guarantee.
  • A select does not choose the expected option. Check the option’s actual value and pass that value to fill(); a displayed label can differ from the underlying value.
  • A handle-based action fails after waiting. waitForSelector() confirms DOM availability, not that a later action will succeed. Use a locator when you need its action preconditions and retry behavior, or check the element’s state before taking the lower-level action.
  • Automation exits with a browser still running. Put browser cleanup in a finally block so browser.close() runs when navigation, filling, or submission throws.

Performance and reliability choices

For a form with several ordinary fields, locators keep the code readable and apply readiness checks at each action. Avoid adding arbitrary sleeps where a locator can wait for its target. If the site needs a specific state beyond locator readiness, wait for that state rather than slowing every run with a fixed delay.

Keyboard-style typing sends events per character, so it has more interaction steps than setting a value with fill(); a nonzero delay adds further time. Use it when event behavior requires it, not as a universal substitute. Direct evaluation can be concise, but it shifts responsibility for control checks and application event behavior to your code. For reliability, keep selectors specific, check important outcomes, and ensure browser cleanup happens on success and failure.

Or skip the browser setup

If your task is to capture a page rather than fill its form, ScreenshotNeo can return a screenshot or PDF with one GET request. It does not perform Puppeteer input automation, so keep Puppeteer for the form interaction above. ScreenshotNeo is a website screenshot API and MCP server from Yorker Media; see the ScreenshotNeo site and API documentation.

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

Before a capture, it can accept cookie or consent banners as a visitor and remove 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 cost nothing, and the response identifies page verdict and billing status in headers. An MCP server offers take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients. The free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Sign up for free and try ScreenshotNeo.

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

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.