What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

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

When a Puppeteer form submission navigates to another document, start page.waitForNavigation() before triggering the submit and await both operations together:

const [response] = await Promise.all([
  page.waitForNavigation({ waitUntil: 'load' }),
  page.click('button[type="submit"]')
]);

This ordering prevents a race in which a fast navigation finishes before Puppeteer begins waiting. If the form uses fetch, XHR, or client-side rendering instead of navigation, do not use waitForNavigation() as your success signal; wait for the response or the visible result your application actually produces.

Why form submission needs an explicit wait

A browser can appear to have submitted a form while Puppeteer is still on the old document, waiting for scripts, or displaying an error. “Page loaded” can mean several different milestones:

  • The new document has been parsed (domcontentloaded).
  • The document and its subresources have fired the load event.
  • Network traffic has become quiet for a defined interval.
  • The application has displayed a success message or completed an API operation.

Choose the milestone that represents completion for your test. A network-idle event is not proof that a business operation succeeded, and a load event does not guarantee that a single-page application has finished rendering its result.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Elebase USB to USB C Adapter for iPhone 18 Pro Max,USBC Car Charger Adapter
  • Read Before You Buy — No Video Output: These adapters support charging and USB 2.0 data transfer, but cannot transmit video signals. Except for standard USB webcams (which use USB data only), they are not compatible with HDMI/DisplayPort cables, video-capable USB-C hubs, or docking stations with video output.
  • Convert USB-A Ports to USB-C: Designed to connect USB-C earphones, cables, flash drives, card readers, and other USB-C accessories to standard USB-A ports. Plug-and-play with no drivers or software required.
  • Aluminum Alloy Housing: Built with a sturdy aluminum alloy shell that aids in heat dissipation and protects against daily wear and scratches. Designed to maintain a stable and secure connection.
  • Compact & Travel-Friendly: The ultra-compact design allows the adapter to stay plugged into your device without blocking adjacent ports or adding bulk, reducing wear and tear on your original USB ports.
  • 12-Month Warranty: Backed by a 12-month manufacturer warranty for peace of mind. Designed to meet strict quality control standards for reliable everyday performance.

Waiting for a real navigation

The safe click pattern

Register the wait in the same statement as the action that can navigate. The wait must be created first, which is why Promise.all() is used:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();

try {
  await page.goto('https://example.com/login', { waitUntil: 'domcontentloaded' });
  await page.type('#email', 'user@example.com');
  await page.type('#password', 'correct-horse-battery-staple');

  const [response] = await Promise.all([
    page.waitForNavigation({ waitUntil: 'load' }),
    page.click('button[type="submit"]')
  ]);

  if (response && !response.ok()) {
    throw new Error(`Navigation returned HTTP ${response.status()}`);
  }

  console.log('Current URL:', page.url());
  console.log('Title:', await page.title());
} finally {
  await browser.close();
}

Use the selector for the actual submit control. If your code submits through a keyboard event, form method, or helper function, keep the same shape:

await Promise.all([
  page.waitForNavigation({ waitUntil: 'load' }),
  page.keyboard.press('Enter')
]);

Calling await page.click(...) first and only then calling waitForNavigation() can miss a fast navigation and leave the wait hanging until its timeout.

Submitting the form directly

For a normal HTML form, clicking the submit control is usually preferable because it follows the page’s validation and event handlers. If you deliberately invoke a function that submits the form, pass that action as the second promise:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Anker USB-C Hub, 5-in-1 USB Hub for Laptops, 4K HDMI Multiport Adapter
  • 5-in-1 USB-C Hub: Experience comprehensive connectivity featuring a Power Delivery input, two USB-A 2.0 ports, a USB-A 3.0 port, and an HDMI port. (Note: The USB-C power delivery input port is only for connecting an external wall charger to power your laptop and cannot power peripheral devices.)
  • 90W Pass-Through Charging: Achieve optimal charging with 90W pass-through power to your laptop, supported by a total input of 100W, with the hub reserving 10W for operational efficiency. (Note: Wall charger not included.)
  • Quick Data Transfers: Accelerate your productivity with rapid data transfers using a high-speed 5Gbps USB 3.0 port and two 480Mbps USB 2.0 ports.
  • 4K HDMI Display: Enhance your visual experience with a hub capable of delivering 4K resolution at 30Hz in both mirror and extend modes. Please note that this hub is compatible with MacBook (macOS 12 and newer), Windows 10 and 11, ChromeOS, and laptops equipped with DP Alt Mode and Power Delivery. Note: This device is not compatible with Linux.
  • What You Get: Anker USB-C Hub (5-in-1, 4K HDMI), welcome guide, 18-month warranty, and our friendly customer service.
await Promise.all([
  page.waitForNavigation({ waitUntil: 'load' }),
  page.evaluate(() => document.querySelector('form').requestSubmit())
]);

The action must actually cause a document navigation or reload. If it does not, the navigation promise will eventually time out.

Choosing the waitUntil milestone

waitForNavigation() accepts one lifecycle event or an array. With an array, Puppeteer waits for every listed event.

Value What it means Use it when
domcontentloaded The initial HTML has been parsed and the DOM is available. Your next operation only needs elements created during parsing, and images or other subresources are irrelevant.
load The document’s load event has fired. This is Puppeteer’s default. You need the conventional full document load for a server-rendered destination.
networkidle0 No more than zero network connections for at least 500 ms. The page should be quiet and does not maintain polling, streaming, or other long-lived requests.
networkidle2 No more than two network connections for at least 500 ms. A page has a small amount of continuing traffic but should otherwise be idle.

You can combine conditions when that reflects the test:

await Promise.all([
  page.waitForNavigation({ waitUntil: ['domcontentloaded', 'load'] }),
  page.click('form button[type="submit"]')
]);

The 500 ms interval is Puppeteer’s definition of the network-idle signals, not a performance benchmark. Analytics, polling, WebSockets, service workers, or advertisements can prevent an idle condition indefinitely. Prefer a specific UI or API condition when one exists.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #3
Sale
Anker USB C Hub, 7in1 Multi-Port USB Adapter, 4K@60Hz USBC to HDMI Splitter
  • Sleek 7-in-1 USB-C Hub: Features an HDMI port, two USB-A 3.0 ports, and a USB-C data port, each providing 5Gbps transfer speeds. It also includes a USB-C PD input port for charging up to 100W and dual SD and TF card slots, all in a compact design.
  • Flawless 4K@60Hz Video with HDMI: Delivers exceptional clarity and smoothness with its 4K@60Hz HDMI port, making it ideal for high-definition presentations and entertainment. (Note: Only the HDMI port supports video projection; the USB-C port is for data transfer only.)
  • Double Up on Efficiency: The two USB-A 3.0 ports and a USB-C port support a fast 5Gbps data rate, significantly boosting your transfer speeds and improving productivity.
  • Fast and Reliable 85W Charging: Offers high-capacity, speedy charging for laptops up to 85W, so you spend less time tethered to an outlet and more time being productive.
  • What You Get: Anker USB-C Hub (7-in-1), welcome guide, 18-month warranty, and our friendly customer service.

When submission does not navigate

Many modern forms call fetch() or XHR, update the current URL with client-side routing, and render a message without replacing the document. In that case, waitForNavigation() is the wrong primitive.

Wait for the API response

const [apiResponse] = await Promise.all([
  page.waitForResponse(response =>
    response.url().endsWith('/api/account') &&
    response.request().method() === 'POST'
  ),
  page.click('button[type="submit"]')
]);

if (!apiResponse.ok()) {
  throw new Error(`Form API failed: ${apiResponse.status()}`);
}

const result = await apiResponse.json();
console.log(result);

Match the URL, HTTP method, or response status narrowly enough that an unrelated request cannot satisfy the wait. If the endpoint returns non-JSON data, inspect apiResponse.text() instead.

Wait for the resulting UI state

await Promise.all([
  page.waitForSelector('[role="alert"][data-status="success"]', {
    visible: true
  }),
  page.click('button[type="submit"]')
]);

For text that is inserted or changed rather than newly created, use waitForFunction():

await Promise.all([
  page.waitForFunction(() => {
    const message = document.querySelector('.status');
    return message && message.textContent.includes('Saved');
  }),
  page.click('button[type="submit"]')
]);

A confirmation element tied to the actual operation is generally more meaningful than waiting for network silence.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
Sale
UGREEN USB to USB C Adapter Combo 4-Pack, 10Gbps USB C Converter Space Gray
  • Dual Converters, Infinite Potential:Includes 2× USB C male to USB A female adapters and 2× USB A male to USB C female adapters. Perfect for a wide range of uses—tablets with Bluetooth keyboards, expand USB ports on macbook, and more. Two different converters for all your daily needs
  • Next-Level 10Gbps & 3A Charging: No more slow 480Mbps, this usb to usb c adapter has a transfer speed of up to 10Gbps, allowing you to do more transferring in less time. This usb adapter fits both USB A and USB C charger, supporting up to 3A fast charging
  • Upgraded Exquisite Craftsmanship: With an aluminum alloy housing and metal connector, the usbc to usb adapter is extremely durable and sturdy. Rigorously tested to withstand more than 10,000 times of plugging and unplugging, ensuring long-lasting performance
  • Broad Compatible: The usb c to usb adapter widely supports all USB C/ USB A devices like laptops, tablets, cellphones, car chargers, and phone chargers. Such as compatible with MacBook Pro/Air 2023/2022, Thunderbolt 4/3 Devices,Apple MagSafe Watch 9/8/7/SE/Ultra, iPad Pro 2022/2021, Samsung Galaxy S23/S20/S10, and iPhone 17/16/15 Pro. Plug and play
  • Please Note: To reach 10Gbps speed, keep the cable under 3.3 ft. For USB A Male to USB C adapters, try flipping the USB C connector. USB C Male to USB A adapters support bidirectional 10Gbps transfer within 3.3 ft

History API, anchors, and the response value

Puppeteer treats History API URL changes as navigation. An anchor-only change or same-document navigation can resolve with a null response because no new main resource was fetched. Therefore, do not treat a null response by itself as a failed wait:

const [response] = await Promise.all([
  page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
  page.click('#continue')
]);

if (response) {
  console.log('Main resource status:', response.status());
} else {
  console.log('Same-document navigation; verify the URL or UI state.');
}

await page.waitForFunction(expected => location.pathname === expected,
  {}, '/account');

For a full navigation, checking response.ok() or response.status() lets you distinguish a loaded error page from a successful HTTP response. HTTP success still does not guarantee that the form’s business action succeeded; verify the page’s result as well.

Timeouts and bounded waits

Puppeteer’s documented wait methods use a 30-second default timeout. Change it globally or for navigation when the target environment justifies a different bound:

page.setDefaultNavigationTimeout(60_000);

await Promise.all([
  page.waitForNavigation({ waitUntil: 'load', timeout: 45_000 }),
  page.click('button[type="submit"]')
]);

setDefaultNavigationTimeout() covers waitForNavigation() and related navigation methods. Keep a finite timeout so a broken site, blocked request, or selector mistake fails with a useful diagnostic. Increase it for known slow environments rather than disabling it without a specific reason.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value
Anker USB C Hub, 5-in-1 USBC to HDMI Splitter with 4K Display
  • 5-in-1 Connectivity: Equipped with a 4K HDMI port, a 5 Gbps USB-C data port, two 5 Gbps USB-A ports, and a USB C 100W PD-IN port. Note: The USB C 100W PD-IN port supports only charging and does not support data transfer devices such as headphones or speakers.
  • Powerful Pass-Through Charging: Supports up to 85W pass-through charging so you can power up your laptop while you use the hub. Note: Pass-through charging requires a charger (not included). Note: To achieve full power for iPad, we recommend using a 45W wall charger.
  • Transfer Files in Seconds: Move files to and from your laptop at speeds of up to 5 Gbps via the USB-C and USB-A data ports. Note: The USB C 5Gbps Data port does not support video output.
  • HD Display: Connect to the HDMI port to stream or mirror content to an external monitor in resolutions of up to 4K@30Hz. Note: The USB-C ports do not support video output.
  • What You Get: Anker 332 USB-C Hub (5-in-1), welcome guide, our worry-free 18-month warranty, and friendly customer service.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Common failures and fixes

“Navigation timeout exceeded”

  • Cause: The form never navigates, the click missed the control, or a persistent request prevented the selected network-idle condition.
  • Fix: Confirm the selector, inspect whether the URL changes, and switch to waitForResponse() or a success selector for an in-page form. Use load or domcontentloaded instead of network idle when background traffic is expected.

The wait starts too late

  • Cause: Code awaited the click before creating the navigation wait.
  • Fix: Put both promises in Promise.all(), with the wait expression listed first.

The page loads but the test continues too early

  • Cause: The load event fired before the application fetched data or rendered the confirmation.
  • Fix: Add a response predicate or wait for the specific success element after navigation.

The response is null

  • Cause: The URL changed within the same document, such as through History API or an anchor.
  • Fix: Check page.url() and wait for the destination UI state; do not require a non-null response.

The wait completes on the wrong request

  • Cause: A broad response predicate matched analytics or another request.
  • Fix: Match the exact endpoint and HTTP method, then verify status and the returned payload.

A reliable decision process

  1. Determine whether submission replaces or reloads the document. Observe the URL, navigation events, or form HTML.
  2. If it navigates, create waitForNavigation() before the click and coordinate both with Promise.all().
  3. Select domcontentloaded, load, or a network-idle event according to the milestone your test needs.
  4. If it stays in place, wait for the specific API response or confirmation UI.
  5. Use a finite timeout, inspect status codes, and verify the business result rather than only browser activity.

Or skip the browser setup

If your goal is a clean image or PDF of the resulting page rather than interactive form testing, ScreenshotNeo provides a single screenshot request. Its cleaner accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing state.

For example, this cURL request captures Stripe as WebP:

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

See the complete options and authentication details in the ScreenshotNeo documentation. You can also use the supplied Python or Node.js clients:

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}`);

ScreenshotNeo also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Every plan includes its capture options, including full-page and element shots, device and retina settings, custom CSS and JavaScript, waits, request blocking, cookies and headers, geolocation, caching, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, and a usage API. The Free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

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

Performance and reliability notes

  • Use the earliest lifecycle event that satisfies the assertion; waiting for unnecessary images or idle periods increases test time.
  • Use UI- or response-based predicates for single-page applications, because they describe completion more directly than global network quiet.
  • Keep selectors stable by using semantic attributes or dedicated test IDs instead of fragile CSS paths.
  • Capture diagnostics on failure: URL, title, console errors, status codes, and a screenshot or HTML dump where permitted.
  • Keep navigation and application timeouts separate when their expected durations differ.

Frequently Asked Questions

Does waitForNavigation wait for JavaScript rendering?

It waits for navigation or reload according to the selected lifecycle event. It does not guarantee that later client-side rendering or an API operation has finished; wait for that observable state separately.

Can I use waitForNetworkIdle instead?

You can, but network idle only describes a quiet period. It may never occur on pages with polling or persistent connections and does not prove that the form succeeded.

Should I disable the timeout for slow pages?

Usually no. Set a finite timeout appropriate to the environment so failures remain detectable and diagnosable.

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.

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.