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

Use page.goto(url) when you already know the destination. When a link click should navigate to another document, start page.waitForNavigation() and the click together with Promise.all so the wait is registered before navigation begins. For client-side transitions, wait for the destination content your script actually needs; a URL change may not produce a document response.

Navigate directly when you know the destination URL

page.goto() is Puppeteer’s direct navigation API. Include the URL scheme, such as https://:

await page.goto('https://example.com');

In a complete script, first launch a browser and create a page, then navigate. This example uses Puppeteer’s documented launch-and-page flow:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  try {
    const page = await browser.newPage();
    const response = await page.goto('https://example.com');
    console.log('HTTP status:', response?.status() ?? 'no main-resource response');
    console.log('Final URL:', page.url());
  } finally {
    await browser.close();
  }
})();

goto() resolves with the main resource’s response, or null in documented cases. A null response does not necessarily mean the page failed: some navigation-like changes do not fetch a new main document.

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

Click a link and wait for document navigation

For routine element interaction, Puppeteer recommends Locators. They wait for action preconditions such as visibility, enabled state, viewport placement, and a stable bounding box. If the click is expected to load another document, register the navigation wait and click concurrently:

const [response] = await Promise.all([
  page.waitForNavigation(),
  page.locator('a.my-link').click(),
]);

console.log('Destination URL:', page.url());
console.log('HTTP status:', response?.status() ?? 'no main-resource response');

This order matters. If the click happens before waitForNavigation() is registered, navigation may start first and the wait can miss it. Puppeteer’s Page.click API reference documents the race and the same Promise.all pattern using page.click(selector).

Choose a selector that identifies the intended link

CSS selectors work by default. Puppeteer also supports text, accessibility role and name, XPath, and open Shadow DOM selector options. Prefer a selector tied to the link’s meaningful role or context, and refine it when the page contains multiple matches; an ambiguous selector can click the wrong link.

Handle client-side transitions by waiting for destination state

Single-page applications may update the URL through the History API or navigate to an anchor without loading a new document. waitForNavigation() treats these as navigation, but its result may be null because there is no new main-resource response. Do not use a non-null response as the only proof that the transition succeeded.

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

Instead, wait for an element that represents the state your script needs. Pick a selector from the actual destination page; there is no universal heading or content selector that works across sites:

await page.locator('a.my-link').click();
await page.locator('[data-testid="destination-ready"]').wait();

console.log('Destination URL:', page.url());

Replace [data-testid="destination-ready"] with a selector present when the target view is ready. The official Page.waitForNavigation API reference describes the response behavior for History API and anchor changes.

Use waitForSelector when you need a lower-level DOM wait

page.waitForSelector() is still available when you specifically need to wait for DOM presence, visibility, or a hidden state. It supports a timeout and returns an ElementHandle, or null in the documented hidden case. Unlike Locators, it does not provide Locator-style automatic action retries. Dispose of returned handles when you no longer need them.

const handle = await page.waitForSelector('.result', { visible: true });
if (!handle) {
  throw new Error('Expected visible result was not found');
}
try {
  console.log(await handle.evaluate(element => element.textContent));
} finally {
  await handle.dispose();
}

For normal click-and-interact work, the Page interactions guide recommends Locators; choose the lower-level wait when its explicit DOM wait or handle is useful.

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 missed clicks and waits

  • The navigation wait times out: the click may cause a client-side transition rather than a document load. Wait for a destination-specific element or state instead of requiring a main-resource response.
  • The wait resolves but response is null: a History API or anchor change can count as navigation without a new main-resource response. Check page.url() and the destination content.
  • The script clicks the wrong link: the selector may match multiple elements. Narrow it using page context, link text, or an accessible role and name.
  • The click cannot proceed because the element is not ready: Locators check action preconditions and retry as needed. Verify that the selector identifies a real, enabled element in the intended view.
  • A handle-based flow retains stale elements: a DOM change can make an earlier handle unsuitable. Locate the element again after the transition, and dispose handles when finished.

Or skip the browser setup

If you need a screenshot rather than browser interaction, ScreenshotNeo provides a website screenshot API and MCP server. One GET request can return an image or PDF; it does not click through page flows.

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

See the ScreenshotNeo API documentation for request options. It removes cookie banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are not billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000. Sign up for free and get 1,000 screenshots a month with no card.

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.