Use page.evaluate() when Puppeteer can return the property as a normal value, evaluateHandle() and getProperty() when you need to keep a reference to an object in the page, and $eval() for a property on a selected DOM element. The right choice depends on where the object lives and whether you need a value or a handle.
Choose the API for where the object lives
| Situation | Use | What you get |
|---|---|---|
| The object can be passed into the page callback, and you want an ordinary result | page.evaluate(fn, arg) |
The callback’s returned value |
| The object exists in the page and you need to retain a reference | page.evaluateHandle(fn) |
A handle to the in-page object |
| You already have a handle and want one property | handle.getProperty(name) |
A handle to the property; call jsonValue() for a serializable value |
| The property belongs to a selected DOM element | page.$eval(selector, fn) |
The callback’s result for the first matching element |
| The element is inside an already selected subtree | elementHandle.$eval(selector, fn) |
The callback’s result for the first matching descendant |
| The object belongs to an iframe | Evaluate through its Frame |
The result from that frame’s context |
The examples below follow the Puppeteer 25.12.0 API reference for Page.evaluate, Page.evaluateHandle, JSHandle.getProperty, Page.$eval, and Frame.evaluate. Documentation labels vary across the inspected pages from 25.1.0 to 25.12.0; check the API docs for the Puppeteer version installed in your project if a signature or type differs.
As an Amazon Associate I earn from qualifying purchases.
Get a plain property value with page.evaluate()
For an object available in Node.js that can be passed to the page callback, return the desired property directly. evaluate() runs its function in the page context, accepts arguments after the callback, and waits for a returned promise to resolve.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesconst obj = { name: 'Ada', active: true };
const value = await page.evaluate(obj => obj.name, obj);
console.log(value); // 'Ada'
Use dot notation for a known property name and bracket notation for a key stored in a variable:
#1 Best Overall
const key = 'name';
const value = await page.evaluate((obj, key) => obj[key], obj, key);
For nested properties that may not exist, optional chaining prevents an exception while traversing a missing intermediate value:
const city = await page.evaluate(obj => obj?.address?.city, obj);
If the property is absent, this returns undefined. Choose an explicit fallback only if it is appropriate for your use case; a fallback can make a missing property indistinguishable from a real property with that value.
Keep a reference to an object in the page
If the object exists only in the page—for example, as window.someObject—create a handle and ask it for the property. Use jsonValue() when you need the property’s serializable value, then dispose of both handles when finished.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →const objectHandle = await page.evaluateHandle(() => window.someObject);
const propertyHandle = await objectHandle.getProperty('propertyName');
try {
const value = await propertyHandle.jsonValue();
console.log(value);
} finally {
await propertyHandle.dispose();
await objectHandle.dispose();
}
evaluateHandle() returns a reference to an in-page object rather than immediately converting it into an ordinary result. getProperty() returns another handle, this time for the named property. jsonValue() returns a vanilla representation of serializable portions; the handle API notes that it does not call an object’s toJSON() method. If the property itself is a complex or non-serializable page object and you need to keep interacting with it, retain its handle rather than assuming jsonValue() preserves the original object. See the official JSHandle and getProperty references.
Get a DOM element’s value with Puppeteer
For an input or another selected element, use $eval() and read the DOM property from the element passed to the callback. This example gets an input’s current value, not its HTML attribute:
const value = await page.$eval(
'input[name="email"]',
el => el.value
);
page.$eval() uses the first matching element and throws if no element matches. If the element is within an existing element handle, use that handle’s $eval() to search among its descendants. Consult the official Page.$eval and ElementHandle.$eval references.
Rank #3
Pass values into the page context explicitly
A callback passed to page.evaluate() executes in the browser page, not in Node.js. A Node.js variable referenced only through the callback’s closure is not automatically available in that separate context. Pass the value as an argument instead:
const wantedKey = 'status';
const status = await page.evaluate(
(obj, key) => obj[key],
{ status: 'ready' },
wantedKey
);
This is why a variable may appear undefined inside page.evaluate(): it was never passed across the context boundary, or the property lookup itself returned undefined. Check both the arguments and the property name. Puppeteer documents callback arguments for Page.evaluate.
Evaluate in the correct frame
Page evaluation uses the page’s context. When the target object is inside an iframe, obtain the relevant Frame and evaluate through that frame, whose evaluation API behaves like page evaluation but uses the frame’s context. A lookup in the top-level page will not automatically reach an object defined inside a separate frame. See Frame.evaluate.
Rank #4
Wait when the value is not ready yet
For simple extraction from an object that is already available, direct evaluation is enough. If retrieval depends on an element becoming available, a Locator can wait for its readiness preconditions: Locator.wait() returns a serialized value and requires JSON serializability, while waitHandle() waits for a handle. Locators add waiting and retry behavior; they are not necessary just to read a property from an object already in hand. See the Locator reference.
Common errors and fixes
- “My Node variable is undefined in
evaluate().” The function runs in the page context. Pass the needed value as an argument rather than relying on a Node closure. $eval()throws because no element was found. The selector matched nothing at evaluation time. Verify the selector and ensure the element exists before calling it; use a Locator when the element’s appearance must be awaited.- The returned value is not the object I expected. Check that you are accessing a property rather than an HTML attribute, and use bracket notation when the property key is dynamic.
- A nested lookup throws. One of the intermediate properties may be null or undefined. Use optional chaining or check each level before reading deeper properties.
jsonValue()loses object behavior or special values. It gives a vanilla representation of serializable portions and does not calltoJSON(). Keep and use a handle when you need continued access to the in-page object.- A handle is no longer usable. Dispose handles after use; navigation or destruction of the execution context also disposes referenced objects.
- An iframe lookup returns nothing or fails. Run the evaluation through the target frame rather than assuming its objects are in the main page context.
Handle lifetime and practical cost
Plain evaluate() is usually the simpler choice when you need only a return value: it avoids retaining a page-object reference. A handle is useful when you need to perform more operations against the same in-page object, but dispose property and object handles when finished so references do not outlive their use. Puppeteer also notes that navigation or a destroyed execution context automatically disposes handles; see the JSHandle reference.
Free tools Windows power users keep installed
One-click scans. No signup required.
Or skip the browser setup
If your goal is a screenshot rather than retrieving a JavaScript property, ScreenshotNeo can return an image or PDF with one GET request. It removes cookie banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are never billed; and its MCP server lets AI agents take screenshots. Its free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000.
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 API documentation for setup and options. Sign up free for 1,000 screenshots a month, with no card required.
Best Value
Frequently Asked Questions
Should I use `evaluate()` or `evaluateHandle()`?
Use `evaluate()` for an ordinary returned value and `evaluateHandle()` when you need to retain a reference to an object in the page.
How do I get an element’s value with Puppeteer?
Use `page.$eval(selector, el => el.value)` for a selector-matched element; it throws if no element matches.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Quick Recap
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.




