Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversHalloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix Now×
FREEDOM251

Freedom251 › Blog › browser automation

How to Find and Follow Links with Puppeteer

By Freedom251 Team · · 5 min read

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

Use a Puppeteer locator to click a link; use page.$$eval() to collect link destinations. If a click may navigate, start page.waitForNavigation() and the click together with Promise.all() so the navigation wait cannot miss the event.

Find link destinations without clicking

When you need to inspect links rather than follow them, evaluate the anchors in the page and return serializable values such as strings and text:

As an Amazon Associate I earn from qualifying purchases.

const links = await page.$$eval('a', anchors =>
  anchors.map(anchor => ({
    text: anchor.innerText.trim(),
    href: anchor.href,
  }))
);

console.log(links);

anchor.href is the browser-resolved URL, so a relative link such as /pricing is returned as an absolute URL. If you need the literal attribute as written in the markup, read anchor.getAttribute('href') instead. $$eval() runs the function against all matching elements and returns its values; avoid returning DOM nodes themselves.

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

Read one matching link

Use $eval() when you need the first matching anchor. It passes that element to your function and throws if there is no match:

const href = await page.$eval('a.primary-link', anchor => anchor.href);

For a defensive check, query first and handle the absent case:

const link = await page.$('a.primary-link');
if (!link) {
  throw new Error('The expected link was not found');
}
const href = await link.evaluate(anchor => anchor.href);

Click a link with a Puppeteer locator

For interaction, Puppeteer recommends locators. They wait for the target to be available and check action preconditions such as visibility, enabled state, viewport position and a stable bounding box before acting.

await page.locator('a.primary-link').click();

Use a selector that identifies the intended link reliably. CSS is suitable for stable markup such as a.next-page. Puppeteer also supports selector extensions for text, accessibility, XPath and shadow-root traversal; use these when the page structure makes them a better fit, and verify the selector matches the intended link rather than a neighboring control.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option

Wait for navigation caused by the click

Start the navigation wait before the click can trigger it by awaiting both operations concurrently:

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

console.log('Current URL:', page.url());
console.log('Navigation response:', response);

The navigation response can be null. Puppeteer treats History API URL changes as navigation, and hash or History API navigation may not produce a full document response. A successful click therefore does not guarantee a new document or a non-null response.

For single-page application updates

If the result you need is an application-state change rather than a document navigation, wait for that result directly—for example, a destination element to appear or the URL to meet a condition. Use waitForNavigation() when navigation itself is the success condition, not merely because a link was clicked.

Use this complete example

This example navigates to a page, gathers all resolved link URLs and then follows a selected link while waiting for navigation:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    await page.goto('https://example.com');

    const links = await page.$$eval('a', anchors =>
      anchors.map(anchor => ({
        text: anchor.innerText.trim(),
        href: anchor.href,
      }))
    );
    console.log('Links:', links);

    const [response] = await Promise.all([
      page.waitForNavigation(),
      page.locator('a').click(),
    ]);
    console.log('URL after click:', page.url());
    console.log('Navigation response:', response);
  } finally {
    await browser.close();
  }
})();

Replace https://example.com and the click selector with the page and link you intend to automate. If the page has multiple anchors, page.locator('a').click() may act on the wrong one; narrow the selector to a stable, link-specific selector.

Choose the right API for the job

Goal Use Important behavior
Click and follow a link page.locator(selector).click() Recommended interaction path; waits for readiness and checks action preconditions.
Collect many link destinations page.$$eval('a', fn) Runs in the page and returns values for all matches.
Read one matching destination page.$eval(selector, fn) Uses the first match and throws if none exists.
Wait for a navigation event page.waitForNavigation() alongside the click in Promise.all() Response may be null for hash or History API changes.

Troubleshoot link selection and navigation

The selector finds no link

  • Check whether the page has rendered the link before querying it. A locator is generally preferable for an action because it waits for the element to become available.
  • Confirm the selector identifies an anchor, not a button styled to look like a link.
  • If the element is inside a frame, select the appropriate frame before querying it. If it is inside a shadow root, use Puppeteer’s supported shadow-root selector syntax where applicable.
  • For $eval(), handle the no-match case: it throws when no element matches.

The click hits the wrong link

A broad selector may match several anchors. Narrow it to a stable class, attribute or other distinguishing structure. When a page offers a meaningful accessible name or visible text, Puppeteer’s text or accessibility selector options may help make the target explicit.

Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers

The navigation wait hangs or misses the event

Pair the wait and click in the same Promise.all(), with the wait listed before the click. If the click changes client-side state without navigation, wait for the relevant element or URL condition instead.

The wait resolves but the response is null

That can be expected for hash changes or History API URL changes. Check page.url() or the destination state your automation actually needs instead of assuming every navigation returns a document response.

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

A lower-level click fails

page.click(selector) selects the first match, scrolls it into view and clicks its center; it throws if nothing matches. Puppeteer documents waitForSelector() as a lower-level option, but it does not automatically retry an action that fails. Prefer locator-based interaction unless you need a custom element-handle workflow.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If the goal is a screenshot of a destination page rather than interactive browser automation, ScreenshotNeo can return an image or PDF from one GET request. It is a screenshot API, not a replacement for Puppeteer when you need to click through an interactive flow.

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. Cookie and consent banners are accepted and removed, along with known newsletter popups and chat widgets, before capture; those steps can be turned off. Bot checks, blank pages, failed loads and cache hits are not billed, and response headers indicate the page verdict and billing status. An 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. Sign up for free.

Frequently Asked Questions

Does Puppeteer click the first matching link?

A locator selector should identify the link you intend to follow; with a broad selector, narrow it rather than relying on which match is chosen.

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

Can I use Puppeteer to extract links without opening them?

Yes. Use page.$$eval() to return anchor URLs as strings without clicking.

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.

browser automationJavaScriptPuppeteerweb scraping

Leave a Reply

Your email address will not be published. Required fields are marked *

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

Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.