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

Use page.evaluate() in Playwright or Puppeteer to run a function inside the browser page, then await its result in your automation code. Pass any needed values as arguments: the page and your Node.js script run in separate JavaScript contexts. If code must run before the page’s own scripts, use Playwright’s page.addInitScript(); if page code needs to call back into your automation environment, use page.exposeFunction().

Run a function in the page with page.evaluate()

In a headless-browser script, there are two JavaScript environments to distinguish: the automation environment (for example, Node.js) and the page environment (the website’s document). page.evaluate() sends a callback to the page context. That callback can use browser globals such as window and document; its result is transferred back to the automation environment.

For example, with a Playwright page that has already navigated to a page:

const pageTitle = await page.evaluate(() => document.title);
const productCount = await page.evaluate((selector) => {
  return document.querySelectorAll(selector).length;
}, '.product-card');

console.log({ pageTitle, productCount });

The first callback reads a page property. The second accepts a selector from the Node.js side and returns a number. The same basic pattern works with Puppeteer:

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.
const title = await page.evaluate(() => document.title);
const count = await page.evaluate((selector) => {
  return document.querySelectorAll(selector).length;
}, '.product-card');

console.log({ title, count });

The page in these examples must refer to the page object created by the relevant library. The snippets show the evaluation calls, not browser launch or navigation setup; those vary by project and library version.

Pass data across the browser boundary

A callback passed to evaluate() is not simply invoked in the scope where it was written. Playwright and Puppeteer serialize the function for execution in the page. That means a local variable or helper function that exists only in the Node.js caller is not automatically available inside the callback.

Pass inputs as arguments

Put values the page-side function needs in the arguments to evaluate(). Keep the page callback self-contained or define its helper logic inside it.

const selector = '.product-card';
const minimumPrice = 20;

const products = await page.evaluate((selector, minimumPrice) => {
  return Array.from(document.querySelectorAll(selector), (card) => {
    const name = card.querySelector('.name')?.textContent?.trim() ?? '';
    const priceText = card.querySelector('.price')?.textContent ?? '';
    const price = Number(priceText.replace(/[^d.]/g, ''));

    return { name, price };
  }).filter((product) => product.price >= minimumPrice);
}, selector, minimumPrice);

Arguments and results cross a serialization boundary. Prefer plain data such as strings, numbers, booleans, arrays, and objects made from transferable values. A DOM node, function, or other non-serializable return value is not the same as returning a usable object in Node.js. Playwright documents that non-serializable results resolve to undefined, subject to certain additional supported values. Check the API documentation for the exact behavior you need.

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

Return data, not page-owned objects

If you need information from an element, extract the fields you need inside the callback and return those fields. For example, return an element’s text, attributes, or a numeric count rather than trying to pass the element itself back as an ordinary JavaScript value.

const links = await page.evaluate(() => {
  return Array.from(document.querySelectorAll('a'), (link) => ({
    text: link.textContent?.trim() ?? '',
    href: link.href
  }));
});

This produces an array of simple objects that can be used in the automation script. Treat the result as a snapshot of values at evaluation time; later page changes do not turn that returned data into a live reference to the page.

Use async functions and wait for page-side work

Both Playwright and Puppeteer await a Promise returned by the evaluation callback. You can therefore use async callbacks for page-side asynchronous work and await the result of page.evaluate() in the automation environment.

const pageData = await page.evaluate(async () => {
  const response = await fetch('/api/page-data');
  if (!response.ok) {
    throw new Error(`Request failed: ${response.status}`);
  }
  return response.json();
});

This callback’s fetch() runs from the page context. It is subject to the browser page’s environment and web security rules; it is not a general-purpose replacement for an HTTP client in Node.js. Make sure the URL is appropriate for the page and that the site permits the request. If your operation depends on a visible UI state or a particular element appearing, wait for that condition using the library’s page-waiting APIs rather than assuming that navigation alone means the page is ready.

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

Choose the right API for when code must run

Use page.evaluate() for an evaluation-time task

Use it when the page is available and your automation needs to read or change page state, calculate a value, or run a bounded piece of browser-side logic. The callback runs when the evaluation call is made, and you can await its returned value or Promise.

Use page.addInitScript() for code that must run before page scripts

Playwright’s page.addInitScript() is the relevant option when code needs to execute after a document is created but before that document’s own scripts run. This is a timing distinction: calling evaluate() after navigation does not retroactively run code before the site’s scripts. Consult the API page for its exact behavior across navigations and frames in the version you use.

Use page.exposeFunction() when the page needs a callback into automation

If browser-side code must invoke a function implemented in the Playwright environment, use page.exposeFunction() rather than trying to capture a Node.js function in an evaluation callback. Playwright’s documentation says exposed functions survive navigation, while functions provided through evaluation are cleared on top-level navigation.

These APIs solve different direction and timing requirements: evaluate sends work into the page and returns data; an initialization script establishes page code before site scripts; an exposed function lets page code call an automation-side callback.

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

Playwright or Puppeteer?

For the core task—execute page-context JavaScript and receive a result—both reviewed APIs support page.evaluate() and awaiting Promise results. Choose based on the library already used by your project, its language and runtime, and whether you need the timing or callback direction handled by Playwright’s additional APIs. The documentation reviewed here does not establish a general performance or compatibility winner.

Need Playwright Puppeteer
Run a callback in the page context and return a result page.evaluate(); callback can use page globals. page.evaluate(); callback executes in the page context.
Pass values into the callback Pass them as evaluation arguments; caller-local variables are not page globals. Pass them as evaluation arguments; the serialized callback cannot use caller-only lexical variables or helpers.
Await a Promise returned by the callback Supported. Supported.
Run code before the document’s own scripts page.addInitScript() is documented for this timing requirement. Not stated in the Puppeteer evaluation source cited here; consult the Puppeteer API documentation for the matching facility in your version.
Let page code call an automation-side function page.exposeFunction(); the Playwright page API documents navigation behavior. Not stated in the Puppeteer evaluation source cited here; consult the Puppeteer API documentation for the matching facility in your version.

The API details above are based on official Playwright and Puppeteer documentation accessed September 29, 2026; the pages did not state publication dates or exact framework release versions. Check the linked API documentation for the version installed in your project: Playwright: Evaluating JavaScript, Playwright: Page API, and Puppeteer: Page.evaluate().

Troubleshoot common evaluation problems

  • A variable is undefined inside the callback. The callback runs in the page context, not the Node.js lexical scope. Pass the value as an argument or define the needed logic within the callback.
  • The result is undefined or unusable in Node.js. Check that the callback returns a value and that it is transferable. Extract simple data in the page rather than returning a page-owned object or function.
  • The result arrives before the site is ready. Evaluation runs when called; it is not automatically a wait for every application-specific condition. Wait for the selector or state your task requires before evaluating.
  • The callback’s asynchronous work is not reflected in the result. Return or await the Promise within an async callback, and await the outer page.evaluate() call.
  • Code runs too late to affect site startup. Use Playwright’s page.addInitScript() for the documented before-page-scripts timing requirement instead of evaluating after the document has run.
  • Page code cannot call a Node.js callback. A function in the automation scope is not automatically callable by the page. In Playwright, expose the function with page.exposeFunction().
  • Behavior differs after navigation. Playwright documents that exposed functions survive navigation, whereas functions provided through evaluation are cleared on top-level navigation. Re-establish page-side setup as needed and verify lifecycle behavior against the API documentation for your version.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability, and cost considerations

Evaluation is most useful for work that genuinely needs the page’s DOM or browser globals. Keep the callback focused: doing extensive extraction and returning compact data avoids unnecessary cross-context transfer. A Promise can be awaited, but that does not itself guarantee the operation will complete; a page-side request can fail, a selector may never appear, or application code may remain pending. Define appropriate waits and failure handling in the surrounding automation flow.

Neither the cited evaluation documentation nor the examples here establishes comparative execution speed for Playwright versus Puppeteer. Measure your own workload if performance is a deciding factor, using the same page, browser configuration, and task. These APIs are software-library features; a physical product is not required to run a custom page function.

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

Or skip the browser setup

If your goal is a screenshot rather than arbitrary JavaScript automation, ScreenshotNeo can return a screenshot or PDF from one GET request. For example, this cURL request saves a WebP capture of Stripe:

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 ScreenshotNeo documentation for API details. Its clean-capture options accept cookie or consent banners and remove more than 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, and response headers identify the page verdict and billing status. It also provides an MCP server with screenshot, page-info, and PDF tools for AI agents. The Free plan includes 1,000 shots a month without a card; paid plans start at $5 for 3,000 shots. Sign up free for 1,000 screenshots a month, with no card required.

Frequently Asked Questions

Can I use variables declared in Node.js inside an evaluation callback?

Not unless you pass them to the callback as arguments; the callback runs in the browser page context.

Does page.evaluate() wait for an async callback?

Yes. Both Playwright and Puppeteer await a Promise returned by the callback, and your automation code should await the evaluate call.

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

Is Playwright faster than Puppeteer for custom functions?

The official API pages covered here do not establish a general speed ranking; performance depends on the workload and setup.

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.