Pass the object as an argument to page.evaluate(), then assign that argument to a property on window:
const data = { userId: 123, featureFlags: { newHeader: true } };
await page.evaluate(data => {
window.myData = data;
}, data);
The callback executes in the browser’s page context. Its final argument is serialized across the Node.js-to-browser boundary, so the page receives ordinary data rather than a reference to your Node.js variable. Use evaluateOnNewDocument() instead when the value must exist before the site’s own scripts run.
Inject an object into the current document
page.evaluate() is the normal choice when the document is already loaded (or at least created) and you want to make data available immediately. Puppeteer runs the supplied function in the page context and passes the values after the function as arguments.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
const data = {
userId: 123,
featureFlags: {
newHeader: true,
compactCheckout: false
},
roles: ['editor', 'analyst']
};
await page.evaluate(data => {
window.myData = data;
}, data);
const valueSeenByThePage = await page.evaluate(() => window.myData);
console.log(valueSeenByThePage);
await browser.close();
Inside the callback, use browser-compatible JavaScript. The callback cannot automatically see variables declared in Node.js; data is available because it was explicitly passed as an argument. The returned object from the final check is serialized back to Node.js.
Free tools Windows power users keep installed
One-click scans. No signup required.
#1 Best Overall
What crosses the boundary
Keep the injected value to data that Puppeteer can serialize: strings, numbers, booleans, null, arrays, and plain objects composed of those values. Do not expect a Node.js function, class instance, socket, stream, or other live object to become usable in the page. Convert special values into a representation your page understands, or use a different API for a browser-side handle or a Node bridge.
Property names are yours to choose. A less collision-prone name such as window.__appConfig can be useful in a controlled test page, while a documented application name such as window.myData is easier for page code to consume. Assigning the property replaces any previous value at that name.
Make the object available before site scripts run
If an application reads the global during startup, setting it after goto() can be too late. Register a function with page.evaluateOnNewDocument() before navigation:
const data = {
theme: 'dark',
userId: 123
};
await page.evaluateOnNewDocument(data => {
window.myData = data;
}, data);
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
The callback runs after a document is created but before that document’s page scripts execute. Puppeteer also invokes it again when the page navigates and for child frames that attach or navigate. This makes it suitable for a value needed on every document, not just the first one.
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 glitchesAccount for repeated execution
Because the hook runs repeatedly, make the assignment idempotent. If site code can replace the property, decide whether your hook should restore it on each navigation or whether the application should own the property after initialization. Avoid side effects such as incrementing a counter in the hook unless you specifically want one per document.
Rank #2
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
Register the hook before goto() and before any later navigation. A navigation creates a new JavaScript world for the document; a value assigned with ordinary evaluate() does not survive that replacement.
Choose the right Puppeteer API
| Need | API | Result and timing |
|---|---|---|
| Set a value in the current document | page.evaluate() |
Runs now in the current page context and passes serializable arguments. |
| Expose a value before application startup | page.evaluateOnNewDocument() |
Runs after document creation and before page scripts; repeats for navigations and child-frame documents. |
| Let page code call Node.js | page.exposeFunction() |
Installs a callable function on window. Calls return a Promise resolved from the Node.js callback. |
| Retain an in-page object reference | page.evaluateHandle() |
Returns a JavaScript handle instead of copying an ordinary value into Node.js. |
When exposeFunction() is the better fit
page.exposeFunction(name, callback) is a Node bridge, not a static object injection technique. The page receives a function at window[name]; when page code calls it, Puppeteer executes the Node.js callback and resolves a Promise with its result.
await page.exposeFunction('getServerFlag', async () => {
return { enabled: true, source: 'node' };
});
const result = await page.evaluate(async () => {
return await window.getServerFlag();
});
console.log(result);
The exposed function survives navigations. Use it for operations that must call back into Node.js—such as obtaining a fresh value or performing a controlled service operation—not for placing a one-time configuration object on window.
When to use evaluateHandle()
page.evaluateHandle() is useful when the result should remain represented by a browser-side object. Instead of copying a serializable result into Node.js, Puppeteer returns a JavaScript handle:
const handle = await page.evaluateHandle(() => ({
status: 'ready',
createdAt: Date.now()
}));
const json = await handle.jsonValue();
console.log(json);
await handle.dispose();
Handles refer to objects in the page’s JavaScript context. Dispose them when finished. They are also invalidated when the associated frame navigates or its execution context is destroyed, so do not retain a handle across a navigation and assume it still points to the same object.
Rank #3
Frames, navigation, and execution contexts
Main page versus an iframe
page.evaluate() runs in the main frame unless you call the corresponding method on a Frame. To inject into an iframe, select its frame and evaluate there:
const frame = page.frames().find(f => f.url().includes('/checkout'));
if (!frame) throw new Error('Checkout frame was not found');
await frame.evaluate(data => {
window.myData = data;
}, { userId: 123 });
A main-frame global is not automatically visible inside a cross-document iframe. For all future child-frame documents, register evaluateOnNewDocument() on the page; Puppeteer documents that the new-document callback is invoked for newly attached or navigated child frames as well.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Navigation races
Do not start a navigation and then assume a previous evaluate() assignment remains. Wait for the navigation, then inject again, or install the new-document hook before navigation. If a site performs client-side routing without replacing the document, the existing global normally remains, but code that explicitly clears it can still remove the property.
Serialization and security considerations
- Dates and custom classes: send an ISO string or plain fields and reconstruct them in the page if needed.
- Undefined and unsupported values: normalize optional fields before injection; do not rely on a function or complex prototype crossing the boundary.
- Secrets: anything placed on
windowis readable by page scripts and browser extensions with access to the page. Never inject credentials unless the page is fully trusted and the exposure is intentional. - Mutability: the page receives its own serialized value. Mutating
datain Node.js later does not automatically updatewindow.myData; inject again or expose a function that returns fresh data. - Property collisions: choose a namespaced property and check whether the application already defines it before overwriting.
Common failures and fixes
“My Node variable is undefined in the callback”
Cause: page functions do not close over ordinary Node.js variables. Fix: pass the variable as an argument:
await page.evaluate(value => {
window.myData = value;
}, data);
“The application reads window.myData before it exists”
Cause: injection happened after startup code ran. Fix: call evaluateOnNewDocument() before goto() or any navigation that creates the target document.
Rank #4
- 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 value disappeared after navigation”
Cause: navigation replaced the execution context. Fix: use the new-document hook for a value required on later documents, or run evaluate() after each navigation.
“The iframe cannot see the global”
Cause: the iframe has its own JavaScript context. Fix: evaluate on the intended Frame, or rely on the page-level new-document hook for child-frame documents.
“Puppeteer reports a serialization error”
Cause: the object contains unsupported values, circular references, or a value that cannot be transferred. Fix: reduce it to a plain JSON-shaped object, remove circular links, and convert special types to strings or numbers before calling evaluate().
“A handle became unusable”
Cause: its frame navigated or the handle was disposed. Fix: create a new handle in the current document and explicitly dispose handles that are no longer needed.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Debugging the injected value
Verify from inside the page rather than only logging the Node-side source object:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Best Value
const check = await page.evaluate(() => ({
exists: Object.prototype.hasOwnProperty.call(window, 'myData'),
value: window.myData,
type: typeof window.myData
}));
console.log(check);
For startup timing, place the check in the page’s own initialization path or use a test page that records the value as soon as its script executes. For frame issues, log each frame’s URL and run the check on the specific frame you intend to modify.
Or skip the browser setup
If your goal is to capture the resulting page rather than operate Puppeteer yourself, ScreenshotNeo provides a website screenshot API and MCP server. A single request returns a PNG, JPEG, WebP, or PDF:
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 options. Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot; bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response identifies the page verdict and billing status in headers. Its MCP server includes take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card, and paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
Equivalent ScreenshotNeo calls in Python and Node.js
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));
Frequently Asked Questions
Does assigning to window change the Node.js object later?
No. The argument is transferred to the page context; later Node-side mutations are not live updates.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchCan I inject the same object into every frame?
Use the page-level new-document hook for documents created in child frames, or evaluate directly on a specific Frame when you need precise control.
Should I use a global for authentication tokens?
Generally no. Page scripts can read globals, so use a safer mechanism and inject only non-sensitive test data unless exposure is intentional.
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.

