TL;DR: Usepage.locator(selector).fill(value)for fast, deterministic Puppeteer submit form scripts andpage.type()when the page watches for real keystrokes (autocomplete, anti-bot, live validation). Submit by clicking the button, pressing Enter, or callingform.requestSubmit(), and always wait for a concrete success signal instead of a fixed timeout.
Forms are how most useful pages actually do work. Logins, search bars, checkout flows, file uploaders, multi-step onboarding wizards: if you automate the web for testing or scraping, sooner or later you have to drive a form. A Puppeteer submit form workflow looks deceptively simple at first, then slams into the realities of a modern site: re-rendering single-page apps, hidden honeypots, label-only inputs, iframe-trapped editors, and JavaScript that quietly throws your input away because it never saw a real keydown event.
An HTML form is a <form> element wrapping <input>, <select>, <textarea>, and similar controls, with an action attribute and a submit trigger that sends the collected data for processing. That is the easy half. The hard half is making a headless Chrome script behave enough like a person that the page actually accepts the submission and gives you back a usable response.
This guide is the cheat sheet I wish I had when I started shipping Puppeteer scripts to production. We will pick the right API for typing, lock down stable selectors, walk through three submit strategies and when each one breaks, cover every common input type (including custom file pickers and rich text editors), wait for the right success signal, validate the result, and finish with a debugging checklist for the dreaded silent failure.




