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

There are two different jobs people call “using an external script with PhantomJS Node.” To run a standalone PhantomJS file, Node.js starts the PhantomJS executable as a child process and passes the script path and arguments. To add code to a page that PhantomJS already controls, use page.includeJs() for a remote URL or page.injectJs() for a local file. The examples below show both paths, how to pass data safely, how to detect failures, and when to stop using this legacy stack.

First choose the operation you actually need

Keep the execution contexts separate. A Node child process runs PhantomJS as a second program; the JavaScript in that file executes in PhantomJS. By contrast, includeJs and injectJs put code into the web page context, where it can access that page’s DOM and browser APIs. Neither mechanism turns Node source into page code.

Need Use Where code runs Completion signal
Run a PhantomJS file from a Node application Node child_process.execFile() with the PhantomJS binary Separate PhantomJS process Node callback, stdout/stderr, and process exit
Load a hosted script into a page page.includeJs(url, callback) Page context Callback after the load attempt
Load a local file into a page page.injectJs(filename) Page context Boolean return value (true or false)

The command-line documentation covered here is for PhantomJS 2.1.1. PhantomJS 2.1 is described as the latest stable release, and project development is suspended. The old phantomjs-node repository is archived (December 4, 2019) and says development was suspended because PhantomJS support was lacking. Current Node releases, operating systems, TLS stacks, and modern sites are not established by those documents, so treat every example as a legacy pattern and validate it in your own environment.

Run a standalone PhantomJS script from Node.js

1. Install or locate a PhantomJS binary

The phantomjs-prebuilt npm wrapper exposes the installed executable through its path property. Its README demonstrates passing that path to Node’s process API. Because the wrapper and PhantomJS are old, check that installation succeeds on the Node version and operating system you intend to deploy.

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

2. Create the PhantomJS script

Save this as phantom-script.js. PhantomJS receives command-line values through its system arguments API. The first user value is at index 1 (index 0 is the script name). Always end a standalone script with phantom.exit(); otherwise PhantomJS can remain running.

var system = require('system');

if (system.args.length < 2) {
  console.error('Usage: phantomjs phantom-script.js URL');
  phantom.exit(2);
}

var targetUrl = system.args[1];
var page = require('webpage').create();

page.open(targetUrl, function (status) {
  if (status !== 'success') {
    console.error('Could not open ' + targetUrl);
    phantom.exit(1);
    return;
  }

  console.log(JSON.stringify({
    title: page.title,
    url: targetUrl
  }));
  phantom.exit(0);
});

Use a simple, serializable argument such as a URL. If you need several values, pass each as its own process argument rather than concatenating a shell command.

3. Launch it with execFile

Put this Node file beside the PhantomJS script. execFile avoids shell parsing, so spaces and shell metacharacters in arguments are not interpreted as command syntax.

const path = require('path');
const { execFile } = require('child_process');
const phantomjs = require('phantomjs-prebuilt');

const script = path.join(__dirname, 'phantom-script.js');
const targetUrl = process.argv[2] || 'https://example.com';

execFile(
  phantomjs.path,
  [script, targetUrl],
  { maxBuffer: 1024 * 1024 },
  (err, stdout, stderr) => {
    if (stdout) process.stdout.write(stdout);
    if (stderr) process.stderr.write(stderr);

    if (err) {
      console.error('PhantomJS failed:', err.message);
      if (typeof err.code !== 'undefined') {
        console.error('Exit code:', err.code);
      }
      process.exitCode = 1;
      return;
    }

    console.log('PhantomJS completed successfully.');
  }
);

Run it with node run-phantom.js https://example.com. The callback receives an error for a failure to start or a non-zero exit, while captured output is available in stdout and stderr. Set maxBuffer high enough for the output your page can generate; otherwise Node can terminate the child when the buffer is exceeded.

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

Streaming output and explicit exit handling

For long-running scripts or verbose logs, the wrapper also documents a convenience phantomjs.exec(...) interface that exposes output streams and an exit event. With raw Node APIs, use spawn when you need incremental output:

const path = require('path');
const { spawn } = require('child_process');
const phantomjs = require('phantomjs-prebuilt');

const child = spawn(phantomjs.path, [
  path.join(__dirname, 'phantom-script.js'),
  'https://example.com'
]);

child.stdout.on('data', data => process.stdout.write(data));
child.stderr.on('data', data => process.stderr.write(data));
child.on('error', error => console.error('Could not start PhantomJS:', error));
child.on('close', code => {
  if (code !== 0) process.exitCode = code || 1;
});

Pass arguments reliably between Node and PhantomJS

Use one argument per value

Build an array such as [script, url, outputFile, mode]. Do not assemble a string like phantomjs script.js "..." and send it to a shell. Separate arguments preserve spaces and reduce quoting and injection mistakes.

Rank #2
Sale

Validate at the PhantomJS boundary

Check system.args.length, reject missing values, and return a non-zero exit code with phantom.exit(number) when input is invalid. Keep secrets out of command-line arguments when possible: process listings and CI logs can expose them.

Remember the process boundary

Node objects, functions, and closures do not automatically cross into PhantomJS. Serialize data to text (for example, JSON) and parse it inside the PhantomJS script. The same rule applies to page evaluation: values crossing page.evaluate must be simple serializable values; functions, closures, and DOM nodes cannot cross that boundary.

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

Load an external script into a PhantomJS page

Remote URL: page.includeJs(url, callback)

Use this when the script is hosted at a URL. PhantomJS loads and executes it in the page, then invokes your callback after the load attempt.

var webpage = require('webpage');
var page = webpage.create();

page.open('https://example.com', function (status) {
  if (status !== 'success') {
    console.error('Page failed to open');
    phantom.exit(1);
    return;
  }

  page.includeJs('https://cdn.example.com/library.js', function () {
    var result = page.evaluate(function () {
      return typeof window.LibraryName !== 'undefined';
    });

    console.log('Library available:', result);
    phantom.exit(result ? 0 : 1);
  });
});

The callback is the point at which you should interact with the loaded library. A callback does not prove that every function in the library behaved correctly; add page-side checks and an explicit exit status.

Local file: page.injectJs(filename)

Use this when the code is on the machine running PhantomJS. The file does not have to be reachable by the hosted page. If it is not in the current directory, PhantomJS also searches its libraryPath. The method returns true when injection succeeds and false otherwise.

var webpage = require('webpage');
var page = webpage.create();

page.open('https://example.com', function (status) {
  if (status !== 'success') {
    phantom.exit(1);
    return;
  }

  var loaded = page.injectJs('/absolute/path/to/page-helper.js');
  if (!loaded) {
    console.error('Local script injection failed');
    phantom.exit(1);
    return;
  }

  var answer = page.evaluate(function () {
    return window.pageHelper ? window.pageHelper() : null;
  });
  console.log(JSON.stringify(answer));
  phantom.exit(0);
});

When to choose each page API

  • Choose includeJs for a script that is intentionally served by a remote host and should load as page content.
  • Choose injectJs for a local helper, test harness, or script that must not be published at a URL.
  • Choose Node’s child process API when the “external script” is actually a complete PhantomJS program, not page code.

These APIs are not interchangeable: execFile starts another operating-system process, while includeJs and injectJs modify the page controlled by an already-running PhantomJS process.

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

Common failures and fixes

“phantomjs: command not found” or a missing binary

Use the wrapper’s phantomjs.path instead of assuming a global executable. Log that path and verify the file is executable. On a deployment host, confirm the downloaded binary is permitted by its OS and architecture.

Node reports an ENOENT error

The executable or script path is wrong. Resolve the script with path.join(__dirname, ...), print both paths, and check the working directory. A relative path based on the caller’s current directory can fail under a process manager.

The child exits immediately with code 1

Read stderr. Typical causes are a syntax error, missing argument, page-open failure, or a PhantomJS-side exception. Return explicit non-zero codes for those branches so Node can distinguish failure from success.

The process never exits

Ensure every asynchronous branch reaches phantom.exit(), including page-open and script-load failures. A pending timer, request, or event handler can also keep the process alive; remove it or close the page before exiting.

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.

includeJs callback runs but the library is absent

Check the URL, network access, TLS compatibility, redirects, and the page’s console output. Test for the expected global inside page.evaluate rather than assuming a callback means the library initialized successfully.

injectJs returns false

Use an absolute filename or configure libraryPath. Confirm read permissions and that the file exists on the machine running PhantomJS, not merely on the Node development machine.

Modern sites render blank or incorrectly

PhantomJS uses an old browser engine. Its project development is suspended, and the cited material does not establish compatibility with current JavaScript, certificates, or websites. If a site requires modern browser features, migrate to a maintained browser automation tool rather than adding fragile patches.

Operational guidance for a legacy integration

Reliability

  • Set a Node-side timeout and terminate a child that exceeds your service’s deadline.
  • Capture both stdout and stderr, and include the exit code in logs.
  • Make scripts idempotent: a retry should not duplicate an upload or other side effect.
  • Pin the wrapper and binary version in deployment, then test the exact target OS.

Security

  • Pass arguments as an array to execFile or spawn; do not interpolate untrusted input into a shell command.
  • Validate allowed URL schemes and hosts before opening user-supplied URLs.
  • Do not print cookies, authorization headers, or API keys to captured output.

Performance and cost

Each child process has startup and memory overhead. Reuse is not straightforward with standalone PhantomJS scripts, so measure queueing and concurrency on your own host. The supplied PhantomJS documentation provides no current performance benchmark or compatibility matrix; avoid promising throughput based on the examples alone.

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

Or skip the browser setup

If your goal is simply to obtain a clean screenshot or PDF rather than maintain a PhantomJS runtime, ScreenshotNeo provides a one-request API and an MCP server for AI agents. Before capture it accepts cookie/consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page and billing result with X-Page-Verdict and X-Billed headers.

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}`);
const body = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', body);

cURL

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

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)

See the ScreenshotNeo API documentation for the full option set: full-page and element captures, lazy-image loading, dark mode, device presets and custom viewports, retina scale, PDF paper and page controls, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting, and the OpenAPI specification. Existing parameter names used by other screenshot APIs also work, which can simplify migration.

An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients. Every plan includes every feature: 1,000 screenshots per month are free with no card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account to get the 1,000 monthly screenshots.

Frequently asked questions

Can Node require a PhantomJS script directly?

No. The established pattern is to launch the PhantomJS executable as a child process and pass the script filename as an argument.

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

Does includeJs load a local file?

Use injectJs for a local file. includeJs is for a URL.

Why is this guidance labeled legacy?

PhantomJS development is suspended, and the related Node repository is archived. Current runtime and website compatibility must be verified by you.

What crosses a page.evaluate boundary?

Simple serializable values cross reliably; functions, closures, and DOM nodes do not.

Frequently Asked Questions

Can Node require a PhantomJS script directly?

No. Launch the PhantomJS executable as a child process and pass the script filename as an argument.

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.

Does includeJs load a local file?

No. Use injectJs for local files; includeJs accepts a URL.

Why is PhantomJS guidance considered legacy?

PhantomJS development is suspended and the related Node repository is archived, so current compatibility is not established.

What values can cross page.evaluate?

Simple serializable values; functions, closures, and DOM nodes do not cross the boundary.

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.

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