ScreenshotNeo

BlogHow-to

How to Dismiss JavaScript Dialogs in Puppeteer

Dismiss alerts, confirms, prompts, and beforeunload dialogs in Puppeteer with a Page listener. See runnable examples, troubleshooting, and when to accept instead.

By the ScreenshotNeo team4 October 20265 min read

To dismiss a JavaScript dialog in Puppeteer, register a dialog listener on the Page and call await dialog.dismiss(). Add the listener before the page action that may open the dialog.

page.on('dialog', async dialog => {
  console.log(dialog.message());
  await dialog.dismiss();
});

This handles native JavaScript dialogs such as alert, confirm, prompt, and beforeunload. See the official Puppeteer Dialog API reference and Page event documentation.

1. Complete runnable example

Install Puppeteer in a new project with npm install puppeteer. Save the following as dismiss-dialog.js, then run node dismiss-dialog.js. This example opens a page that triggers an alert and dismisses it.

const puppeteer = require('puppeteer');

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

    // Register before navigation or interaction that might show a dialog.
    page.on('dialog', async dialog => {
      console.log(`Dialog type: ${dialog.type()}`);
      console.log(`Dialog message: ${dialog.message()}`);
      await dialog.dismiss();
    });

    await page.goto('data:text/html,<script>alert("Capture ready")</script>', {
      waitUntil: 'load',
    });
    console.log('The page continued after the alert was dismissed.');
  } finally {
    await browser.close();
  }
})().catch(error => {
  console.error(error);
  process.exitCode = 1;
});

The handler is attached to the Page, because Puppeteer emits this event on the page that owns the dialog. The callback can inspect the dialog and choose whether to accept or dismiss it.

2. Dismiss every dialog or choose by type

If every encountered dialog should be rejected, the simple listener is sufficient. To make behavior explicit, use type() or message() and handle each case deliberately:

page.on('dialog', async dialog => {
  switch (dialog.type()) {
    case 'alert':
    case 'confirm':
    case 'prompt':
    case 'beforeunload':
      await dialog.dismiss();
      break;
    default:
      // Keep an explicit fallback in case the browser adds a dialog kind.
      await dialog.dismiss();
  }
});

Dialog types documented by Puppeteer include alert, prompt, confirm, and beforeunload. The message() method returns the displayed text; defaultValue() returns a prompt’s default value and an empty string for non-prompts; handled indicates whether a dialog has already been handled. These inspection methods are useful for logging and test assertions, but avoid making automation depend on exact message wording if the site localizes or changes it.

3. Dismiss, accept, or answer a prompt

Use dismiss() to close without accepting. Use accept() when the page should proceed as if the user confirmed. For a prompt, pass the text to submit:

page.on('dialog', async dialog => {
  if (dialog.type() === 'prompt') {
    await dialog.accept('automated response');
  } else {
    await dialog.dismiss();
  }
});

The optional string passed to accept() applies to prompts; it has no effect on other dialog types. Both accept() and dismiss() return promises, so await the chosen action. Refer to Puppeteer’s Dialog.accept() documentation.

Goal Call Behavior
Close without confirmation await dialog.dismiss() Dismiss the dialog.
Confirm or close an alert await dialog.accept() Accept the dialog.
Submit a prompt response await dialog.accept('text') Accept with the supplied prompt text.
Choose a response conditionally dialog.type(), dialog.message() Inspect type or displayed text before responding.

4. Timing and lifecycle

  1. Create the page.
  2. Attach the dialog handler.
  3. Navigate or interact with the page to trigger the dialog.
  4. Await dismiss() or accept() in the handler.

Attaching the listener before the triggering action is practical event-handling guidance based on Puppeteer’s documented event-listener pattern. If the listener is added only after an action that opens a dialog, automation may be waiting on an operation while the dialog is still open. Reuse the handler for subsequent dialogs on that page; if a new page is created, attach a handler to that page too.

5. Native dialogs versus HTML modals

This API handles browser-native JavaScript dialogs surfaced through Puppeteer’s dialog event. A page-built modal, cookie panel, or custom overlay is ordinary webpage content; it does not become a Puppeteer Dialog. Handle those with normal page locators, selectors, or application-specific interaction. If a listener never fires, first check whether the visible prompt is actually a DOM element rather than a native dialog.

6. Troubleshooting

Symptom Likely cause Fix
Navigation or a click appears to hang A native dialog opened and no handler responded. Register page.on('dialog', ...) before the navigation or click, and await accept() or dismiss().
The listener does not run The handler was attached after the trigger, attached to another page, or the prompt is a DOM modal. Attach early on the relevant Page; inspect the page markup and use DOM interaction for HTML overlays.
A prompt receives the wrong value The code dismissed it or accepted without the intended prompt text. Check dialog.type(), then call await dialog.accept('expected value') for a prompt.
Unexpected dialogs are silently accepted or dismissed A blanket handler applies one policy to all dialog kinds. Branch on type() or inspect message(), and log decisions during diagnosis.
Close or navigation behaves differently than expected A beforeunload handler may be involved. Handle it through the same dialog listener and choose accept or dismiss according to the intended navigation behavior.
BiDi deployment behaves differently Dialog interception support can depend on the installed Puppeteer and browser combination. Check the version-specific documentation and verify the deployed combination; Puppeteer’s next-branch guide lists dialog interception support but does not provide a version-by-version matrix.

7. Performance, reliability, and cost

A dialog listener is local event handling; the practical reliability concern is registering it on the correct page before the trigger and ensuring each event is answered. Log type and message when diagnosing a failure, but avoid logging sensitive prompt contents in production. For repeated automation, keep the handler policy in one helper so pages follow a consistent rule. Browser execution, hosting, and any external screenshot or automation service have their own costs; Puppeteer’s dialog API documentation does not specify a price or performance benchmark.

8. FAQ

Does dismiss() return immediately?

It returns a promise that resolves once the dialog has been dismissed. Await it in the event handler.

Can I read the default text in a prompt?

Yes. dialog.defaultValue() provides the prompt default and returns an empty string for non-prompt dialogs.

Does the listener handle a custom JavaScript modal built in HTML?

No. It handles native JavaScript dialogs emitted through the Page event. Interact with a page-rendered modal as DOM content.

Or skip the browser setup

If your goal is a screenshot rather than controlling a dialog in a browser script, ScreenshotNeo offers a website screenshot API and MCP server. One GET request returns an image or PDF; see the ScreenshotNeo API docs.

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, with page verdict and billing details in response headers. 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.

Create a free ScreenshotNeo account to start with 1,000 screenshots per month and no card.