Handle JavaScript Dialogs with Puppeteer
Handle alerts, confirms, prompts, and beforeunload dialogs in Puppeteer with an awaited dialog listener, practical examples, and fixes for common errors.
To handle a JavaScript dialog in Puppeteer, register a dialog event listener on the Page before the action that triggers it, then await dialog.accept() or dialog.dismiss(). For a prompt, pass the text to enter to accept(promptText). This covers alert, confirm, prompt, and beforeunload dialogs.
page.on('dialog', async dialog => {
console.log(dialog.type(), dialog.message());
if (dialog.type() === 'prompt') {
await dialog.accept('example response');
return;
}
await dialog.dismiss();
});
The example enters a sample value for prompts and dismisses every other dialog. Those are example policies, not universal defaults: choose the response that matches the behavior your test is meant to verify. The official Dialog API reference documents the event and dialog methods.
1. Install Puppeteer and prepare a page
Use the Puppeteer version already selected by your project, and consult its matching API docs if behavior or types differ from the current rolling reference. The Puppeteer project page distinguishes puppeteer, which downloads a compatible Chrome during installation, from puppeteer-core, which does not download Chrome. If a package manager blocks install scripts, follow Puppeteer’s browser installation instructions for your environment.
npm install puppeteer
Here is a complete runnable CommonJS example. It opens a local page that displays an alert, registers the handler first, triggers the dialog, waits for the action, and closes the browser.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
page.on('dialog', async dialog => {
console.log(`Dialog type: ${dialog.type()}`);
console.log(`Message: ${dialog.message()}`);
await dialog.accept();
});
await page.goto('data:text/html,<button onclick="alert(\'Saved\')">Save</button>');
await page.click('button');
console.log('The alert was accepted.');
} finally {
await browser.close();
}
})().catch(error => {
console.error(error);
process.exitCode = 1;
});
The listener is attached to the specific Page that will show the dialog. Register it before click, evaluate, navigation, or another action that can trigger the dialog. The event-based API and official example establish page-level handling; registering early is the practical way to ensure the handler is ready.
2. Choose accept or dismiss
Accepting proceeds through the dialog’s affirmative path; dismissing takes its cancellation path. Both methods return promises, so await them in an asynchronous listener. For prompt, accept('text') supplies the entered value. That argument has no effect for other dialog types. See the official references for accept and dismiss.
| Dialog | Typical handling choice | What to consider |
|---|---|---|
alert |
Accept to clear it; dismiss also closes it | It has no affirmative/cancel choice, so select either action your flow supports. |
confirm |
Accept to confirm, dismiss to cancel | Pick the outcome the test is verifying, especially before destructive actions. |
prompt |
Accept with a value, or dismiss | Supply the intended test input; omitting the string accepts without custom input. |
beforeunload |
Accept to proceed with leaving, dismiss to stay | Choose based on whether the test is exercising navigation or preserving the page. |
Puppeteer exposes dialog.type(), dialog.message(), and dialog.defaultValue(). The default value is the prompt’s default, or an empty string for a non-prompt dialog. These are useful for logging and for tests that intentionally derive prompt input from the displayed default. See the official references for type, message, and defaultValue.
Handle each type explicitly
A deliberate policy prevents a test from silently accepting an unexpected confirmation. This example accepts alerts, supplies a value only for prompts, and dismisses confirmations and beforeunload dialogs. Change the branches to reflect the test’s intended outcome.
page.on('dialog', async dialog => {
const type = dialog.type();
console.log({ type, message: dialog.message(), defaultValue: dialog.defaultValue() });
if (type === 'alert') {
await dialog.accept();
} else if (type === 'prompt') {
await dialog.accept('test input');
} else {
await dialog.dismiss();
}
});
Assert the resulting page behavior
Handling the dialog only resolves the browser prompt. A useful test also checks the page outcome. For example, after accepting a prompt, assert the page displays the submitted value; after dismissing a confirmation, assert the destructive action did not run. Use the assertion library and selectors appropriate to your project.
3. Keep the listener safe and maintainable
- Attach before triggering. Set the listener before the click or script that opens the dialog.
- Await the response. Await
accept()ordismiss(); do not leave a rejected promise unobserved. - Keep policy local to the test. A reusable listener that accepts every dialog may hide unexpected confirmations. Branch on type and, where relevant, message.
- Do not close a shared browser in the handler. Browser shutdown belongs to the test or application lifecycle unless closing the browser is specifically intended. Puppeteer’s class example closes its browser after handling the dialog, but that is example cleanup, not a requirement for a reusable handler.
- Remove temporary listeners when needed. If a page is reused and a handler should apply only once, use the event-emitter pattern supported by your installed Puppeteer version to remove it after use. Avoid stacking handlers across repeated test cases.
For a prompt, you can inspect the default value before deciding what to enter:
page.on('dialog', async dialog => {
if (dialog.type() === 'prompt') {
const response = dialog.defaultValue() || 'fallback value';
await dialog.accept(response);
return;
}
await dialog.dismiss();
});
4. cURL, Python, and Node.js examples
JavaScript dialogs are handled by browser automation code running against a page; cURL and Python’s requests do not control a browser dialog. These examples show the Puppeteer approach from Node.js, including an ES module version. Use browser automation when the task requires interacting with JavaScript dialog behavior.
Node.js with ES modules
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
page.on('dialog', async dialog => {
if (dialog.type() === 'prompt') {
await dialog.accept('subscriber@example.com');
} else if (dialog.type() === 'confirm') {
await dialog.dismiss();
} else {
await dialog.accept();
}
});
await page.goto('https://example.com');
// Trigger the dialog through the page's intended UI action here.
} finally {
await browser.close();
}
Why cURL and Python requests are not equivalents
A raw HTTP request can fetch a response but does not execute the page in a browser or emit Puppeteer’s dialog event. Use cURL or Python requests for HTTP-level checks; use Puppeteer to exercise the browser interaction. There is no meaningful cURL or requests snippet that accepts a JavaScript dialog.
5. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The click or evaluation appears to hang. | A JavaScript dialog is open and no handler responded. | Attach a dialog listener before the trigger and await accept or dismiss. |
| The handler never runs. | The listener was added after the triggering action, or it is attached to a different page. | Register first on the exact Page instance performing the action. |
| A prompt receives the wrong text. | The handler always accepts with a fixed string, or assumes defaultValue() is the desired test value. |
Choose prompt input from the test case and inspect the default only when that is the intended behavior. |
| A confirmation proceeds when it should cancel. | A generic handler accepts every dialog. | Branch on type() and dismiss the confirmation for the cancellation path. |
| An unexpected confirmation is hidden. | The handler automatically accepts without checking type or context. | Log or assert the type and message, and fail or dismiss when the dialog is outside the expected test flow. |
Cannot find module 'puppeteer' or Chrome is missing. |
The package is absent, installation scripts were blocked, or puppeteer-core is being used without a configured browser. |
Install the intended package and follow Puppeteer’s browser setup for the installed version; configure the executable when using puppeteer-core. |
| TypeScript reports a listener type error. | The handler annotation or imported Puppeteer types do not match the installed package version. | Use the types shipped with that installed version and its matching API reference. |
6. Performance, reliability, and cost
Dialog handling adds a small asynchronous response step to the interaction, but the research sources publish no dialog-specific timing benchmark, so do not budget from an assumed fixed delay. Reliability depends on installing the listener before the triggering action, awaiting the response, and matching accept/dismiss behavior to the scenario. Browser launch and page loading are separate parts of the workflow and can dominate total run time.
For CI, keep browser and Puppeteer versions aligned, make the test’s expected dialog explicit, and ensure browser cleanup runs even if an assertion fails. Puppeteer itself has no per-dialog charge documented in the cited material; compute infrastructure and CI costs depend on your runtime environment.
7. Or skip the browser setup
If the goal is a page screenshot rather than testing dialog behavior, ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns a PNG, JPEG, WebP, or PDF. It does not replace Puppeteer for accepting a JavaScript prompt or asserting the dialog flow, but it can avoid browser setup when you only need a screenshot.
See the ScreenshotNeo API documentation for request options. Minimal cURL request:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Equivalent Python request:
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)
Equivalent Node.js request:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));
Cookie banners, popups, and chat widgets are removed before the shot; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. An MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card required.
8. FAQ
Does Puppeteer automatically accept JavaScript dialogs?
Use a page-level dialog listener and explicitly accept or dismiss the event. This makes the test’s choice visible and intentional.
Can I read the text shown in an alert?
Yes. Call dialog.message() in the handler and use it for diagnostics or an assertion.
Does accept('text') enter text into an alert or confirmation?
No. The string is prompt input; it has no effect on non-prompt dialogs.
Should a dialog handler accept every dialog?
Only when that behavior is appropriate for the test. Otherwise branch on dialog type and choose the outcome the test is exercising.


