October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
browser automation

How to Inject Global Variables Into Puppeteer Pages (and Keep Them Across Navigation)

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.

The short answer: pass a Node.js value as an argument to page.evaluate() when you need it for one operation. If the page must see a global before its own scripts run—and after every navigation—register the assignment with page.evaluateOnNewDocument(). If browser code must call Node.js on demand, use page.exposeFunction().

These APIs solve different timing, lifetime and direction problems. Choosing the right one prevents globals that appear too late, disappear after navigation, or expose more Node.js capability than intended.

How Puppeteer contexts work

Puppeteer controls a browser, but a function supplied to page.evaluate() executes in the page’s JavaScript context, not in Node.js. The two contexts do not share ordinary lexical variables. A Node variable such as config is unavailable inside the browser function unless you pass it as an argument or deliberately create a bridge.

Values crossing the boundary should be JSON-like data: strings, numbers, booleans, arrays, plain objects and null. Functions, class instances, sockets and many host objects do not serialize as ordinary configuration. Pass only the fields the page needs.

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

1. Inject a value for one operation with page.evaluate

Use an explicit argument when the page already exists and the value is needed by one calculation or interaction.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.test');

const config = {
  apiBase: 'https://api.example.test',
  featureFlag: true
};

const result = await page.evaluate((cfg) => {
  window.appConfig = cfg;
  return {
    enabled: window.appConfig.featureFlag,
    api: window.appConfig.apiBase
  };
}, config);

console.log(result);
await browser.close();

The second argument, config, is serialized and supplied to the page function as cfg. Puppeteer waits for a promise returned by the function, so asynchronous page work can be awaited inside it.

Use a return value instead of a permanent global when possible

If the value is needed only to calculate a result, avoid changing window:

const title = await page.evaluate((prefix) => {
  return `${prefix}: ${document.title}`;
}, 'Captured page');

This keeps page state smaller and makes tests easier to reason about. Assign to window only when application code or later evaluations genuinely need a global.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

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

Why closing over Node variables fails

const config = { featureFlag: true };

// Do not rely on this: config is a Node.js variable.
await page.evaluate(() => window.appConfig = config);

The function is transferred to the browser context; its Node.js closure is not. Pass config explicitly, as in the working example.

2. Install a global before site scripts with evaluateOnNewDocument

Use page.evaluateOnNewDocument() when application JavaScript must see the value during startup. Puppeteer runs the supplied function after a document is created but before that document’s scripts execute. Register it before goto().

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const page = await browser.newPage();

const config = {
  apiBase: 'https://api.example.test',
  featureFlag: true
};

await page.evaluateOnNewDocument((cfg) => {
  window.appConfig = cfg;
}, config);

await page.goto('https://example.test', { waitUntil: 'domcontentloaded' });

const flagSeenByPage = await page.evaluate(() => window.appConfig.featureFlag);
console.log(flagSeenByPage); // true

await browser.close();

This hook is invoked for navigations and for child frames when they are attached or navigated. It is therefore the appropriate choice for a global that must be installed repeatedly, rather than assigned once to a document that may soon be replaced.

Navigation replaces a document

A one-time assignment made with page.evaluate() belongs to the current document. A full navigation creates a new document and can remove that assignment:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.goto('https://example.test/first');
await page.evaluate(() => { window.appConfig = { featureFlag: true }; });
await page.goto('https://example.test/second');
// The second document does not inherit the first document's window object.

Register the new-document hook before the first navigation (or before later navigations if the page already exists) to reinstall the global.

Ordering and changing configuration

Each call captures the argument supplied when you register it. If configuration changes, register a new hook or use a page evaluation after navigation. For deterministic startup, register all required hooks before triggering navigation, reloads or actions that cause a new document.

3. Let page code call Node.js with exposeFunction

Sometimes the browser should request current data rather than receive a copied snapshot. page.exposeFunction(name, callback) adds a function with that name to the page’s window. The callback runs in Node.js, and a returned promise is awaited. The exposed function survives navigations.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const page = await browser.newPage();
const config = { apiBase: 'https://api.example.test', featureFlag: true };

await page.exposeFunction('getAppConfig', async () => {
  return config;
});

await page.goto('https://example.test');
const value = await page.evaluate(() => window.getAppConfig());
console.log(value);

await page.goto('https://example.test/another-route');
const valueAfterNavigation = await page.evaluate(() => window.getAppConfig());
console.log(valueAfterNavigation);

await browser.close();

This is a callable bridge, not a passive global. The browser invokes it and receives a serialized result. Because the callback can perform Node.js work, expose only capabilities that the page is meant to use.

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

Validate arguments at the Node boundary

await page.exposeFunction('lookupFeature', async (name) => {
  if (typeof name !== 'string' || !/^[a-z0-9_-]+$/i.test(name)) {
    throw new Error('Invalid feature name');
  }
  return config.featureFlag && name === 'new-dashboard';
});

Any script able to call the named window function can invoke the callback. Choose a distinctive name, validate every argument, return the smallest useful result, and avoid exposing file-system, process or network operations without strict checks.

Which API should you choose?

Need API Timing and lifetime Direction
One operation or small calculation page.evaluate(fn, value) Runs when called in the current document Node value to page function
Global or shim before application scripts page.evaluateOnNewDocument(fn, value) Runs before page scripts on navigations and child-frame document creation Node value to every new document
Page requests live Node.js data or an action page.exposeFunction(name, callback) Window function persists across navigations Page call to Node callback and result back

A practical decision sequence

  1. Ask whether the page must see the value before its own scripts run. If yes, use evaluateOnNewDocument.
  2. If timing is not critical and the value is used once, pass it to evaluate.
  3. If the page needs to request changing data or a Node-side operation, expose a narrowly scoped function.
  4. For frames, decide whether the global belongs in every child frame. New-document hooks apply there; a top-level evaluate assignment does not automatically populate another frame.

Serialization, frames and security edge cases

Keep payloads serializable and minimal

Convert configuration to plain data before crossing contexts. Instead of passing a class instance, send the required fields. Do not put secrets in a browser global: page scripts, third-party scripts and extensions with page access may read it. Prefer short-lived values and server-side authorization where possible.

Top frame versus child frames

page.evaluate() runs in the page’s selected frame. To target another frame, obtain the frame and evaluate there. A new-document hook is applied when child frames are attached or navigated, so it is the safer mechanism when every frame must receive the same bootstrap value. Cross-origin restrictions still apply to what your page code can access inside a frame.

Reloads, redirects and single-page routing

Reloads and server redirects create documents and trigger the new-document hook. Client-side route changes in a single-page application may not create a document; the existing global remains unless application code changes it. If a route-specific value is required, update it deliberately with page.evaluate or have page code call an exposed function.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Common failures and fixes

Symptom Likely cause Fix
ReferenceError: config is not defined A page function tried to close over a Node variable. Pass it as an argument: page.evaluate(fn, config).
Application reads undefined during startup The global was assigned after page scripts ran. Register evaluateOnNewDocument before navigation.
Global disappears after goto or reload The old document was replaced. Install a new-document hook; do not rely on a one-time assignment.
Exposed function is missing It was called before registration, or a different page/browser context is being used. Await page.exposeFunction before navigation and call it on that same page.
Callback receives unexpected input Browser code can call the bridge with arbitrary values. Validate types and allowed values in Node.js and throw on invalid input.
Value is present in the top page but not an iframe The evaluation targeted only one frame. Evaluate in the intended frame or use a new-document hook for frame bootstrapping.
Serialization error or missing fields The payload contains unsupported objects or more data than intended. Map it to a plain JSON-like object before passing it.

Testing and operational guidance

  • Test a first load, a full reload, a redirect and a second URL when persistence matters.
  • Assert the value from inside the page with page.evaluate, rather than trusting Node’s original object.
  • Test child frames separately if the site embeds them.
  • Register hooks before any action that can navigate, including clicks that submit forms.
  • Log the hook registration point and navigation URL when diagnosing race conditions; avoid logging secret values.
  • Keep exposed callbacks fast and bounded. If they perform asynchronous work, handle rejection so page code receives a predictable error.

Or skip the browser setup

If your goal is a clean image or PDF rather than browser automation, ScreenshotNeo accepts one GET request and returns a PNG, JPEG, WebP or PDF. It can accept cookie and consent banners before capture, remove more than 60 known consent platforms plus newsletter popups and chat widgets, and lets you turn those steps off. Only clean shots are billed; bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, with the outcome reported in X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients.

See the complete parameter reference in the ScreenshotNeo documentation. A basic request is:

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

Equivalent Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Equivalent Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Every feature is included on every plan. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to start.

FAQ

Does evaluateOnNewDocument run before every script?

It runs after the document is created and before that document’s scripts execute, including newly created or navigated child frames.

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

Can an exposed function return a promise?

Yes. Puppeteer awaits the returned promise and delivers its resolved value to the page, or reports its rejection as an error.

Should secrets be injected as globals?

No. Anything placed on window can be inspected by page JavaScript. Keep secrets in Node.js and expose only narrowly validated operations when necessary.

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.

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.

Read next

Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
PC Slower Than It Used to Be?Free scan - under a minute

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.