Use PhantomJS to capture a JavaScript-heavy page by opening the URL, checking the page.open status, waiting for the page’s own asynchronous content to become ready, and then calling page.render before phantom.exit(). PhantomJS runs page JavaScript by default, but its load callback only indicates that the document load completed—not that a modern single-page application has finished fetching and painting every component. Treat PhantomJS as a legacy option: development is suspended, and the official repository has been archived read-only since May 30, 2023.
What PhantomJS can—and cannot—capture
PhantomJS is a command-line, headless browser. Its documented capture flow is intentionally small: create a WebPage object, set the viewport, call page.open(url, callback), render with page.render(filename), and terminate with phantom.exit(). The official Quick Start shows the same sequence.
JavaScript is enabled by default according to the settings reference. That lets PhantomJS execute scripts, draw Canvas and SVG, and load images. However, page.open invokes its callback when the page load process finishes. Applications that fetch data after that point can still be incomplete when you render. There is no documented, universal “all application work is finished” event for every framework, so readiness must be decided for the target site.
Compatibility is the main qualification. The project homepage states, “Important: PhantomJS development is suspended until further notice.” The official GitHub repository identifies 2.1 as the latest stable release and is archived read-only (archive date: May 30, 2023). Current browser APIs, fonts, security policies, bot checks, and JavaScript bundles may therefore behave differently—or fail entirely. Verify the exact pages you need, and choose a maintained browser automation stack when current web-platform compatibility is a requirement.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errors#1 Best Overall
Install and run a minimal capture
1. Put PhantomJS on your PATH
Install the PhantomJS executable for your operating system, then confirm that the command is available:
phantomjs --version
The Quick Start runs a script with phantomjs hello.js. Keep the executable and script in a controlled build environment if captures must be reproducible; PhantomJS itself is no longer receiving development updates.
2. Create the capture script
var page = require('webpage').create();
page.viewportSize = { width: 1280, height: 900 };
page.open('https://example.com', function (status) {
if (status !== 'success') {
console.log('Failed to load the page');
phantom.exit(1);
return;
}
// Add a site-specific readiness test or a deliberate delay here
// when content arrives after the initial page load.
page.render('capture.png');
phantom.exit();
});
3. Execute it and inspect the output
phantomjs capture.js
A successful run writes capture.png in the current directory and exits with status 0. A failed page.open call prints the message and exits with status 1. Use an absolute output path in scheduled jobs so the destination does not depend on the process’s working directory.
Make asynchronous content ready before rendering
Use a fixed delay only when the page is predictable
The project homepage demonstrates opening a page, waiting with a short timeout, and then capturing. A delay is easy to add, but its duration is an example rather than a universal PhantomJS setting. If the network is slow, the delay can be too short; if the page is fast, it wastes time.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated 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 matchvar page = require('webpage').create();
page.viewportSize = { width: 1280, height: 900 };
page.open('https://example.com/dashboard', function (status) {
if (status !== 'success') {
console.log('Open failed: ' + status);
phantom.exit(1);
return;
}
window.setTimeout(function () {
page.render('dashboard.png');
phantom.exit();
}, 3000); // Tune for this page; it is not a universal readiness value.
});
Prefer a page-specific readiness check
When the site exposes a stable marker—such as a table, a “loaded” class, or a known application state—poll that marker from the PhantomJS script and render only after it appears. This is an implementation approach, not a documented universal readiness API.
Rank #2
var page = require('webpage').create();
page.viewportSize = { width: 1280, height: 900 };
var deadline = Date.now() + 15000;
var timer;
function checkReady() {
var ready = page.evaluate(function () {
return !!document.querySelector('[data-capture-ready]');
});
if (ready) {
window.clearInterval(timer);
page.render('ready.png');
phantom.exit(0);
} else if (Date.now() > deadline) {
window.clearInterval(timer);
console.log('Readiness marker did not appear before timeout');
phantom.exit(2);
}
}
page.open('https://example.com/app', function (status) {
if (status !== 'success') {
console.log('Open failed: ' + status);
phantom.exit(1);
return;
}
timer = window.setInterval(checkReady, 250);
});
Choose a marker that represents the content you actually need, not merely the presence of a root element that is rendered before data arrives. If you control the application, adding a capture-only marker is more reliable than guessing a delay.
Understand the difference between resource timeout and readiness
The WebPage settings API includes a resource timeout. It stops an individual requested resource after the configured number of milliseconds; it does not wait for application content to finish rendering. Set it to prevent a stuck request from holding a job forever, but still use a readiness condition or bounded delay for asynchronous UI work.
Configure the page before opening the URL
The settings reference says page settings apply during the initial page.open call, so set them before opening. Common controls include JavaScript, image loading, user agent, resource timeout, and web security.
Free tools Windows power users keep installed
One-click scans. No signup required.
var page = require('webpage').create();
page.settings = {
javascriptEnabled: true,
loadImages: true,
userAgent: 'Mozilla/5.0 (compatible; PhantomJS capture)',
resourceTimeout: 20000
};
page.viewportSize = { width: 1440, height: 900 };
page.open('https://example.com', function (status) {
if (status !== 'success') {
phantom.exit(1);
return;
}
page.render('configured.png');
phantom.exit();
});
JavaScript and image loading are enabled by default, but setting them explicitly documents your intent. A custom user agent can alter server responses; use one only when you understand the site’s behavior. Do not disable web security or ignore TLS problems as a routine screenshot fix. Those switches can hide real production failures and reduce isolation.
Choose viewport, clipping, and output format
Viewport size controls layout
Set page.viewportSize to the CSS viewport you want the site to see. Responsive breakpoints, navigation, and typography can change when width or height changes.
page.viewportSize = { width: 1366, height: 768 };
The viewport is not automatically the full page. A long page may require the screen-capture techniques described in the official screen capture guide, and results remain subject to the legacy rendering engine’s handling of current CSS and web fonts.
Clip a defined rectangle
Use page.clipRect when the artifact should contain only a known region:
page.clipRect = { top: 120, left: 40, width: 900, height: 500 };
page.render('content-area.png');
Coordinates are pixels in the rendered page. A clip rectangle is useful for a chart, a report panel, or a visual-regression region; omit it when you need the normal page capture.
Let the filename select the format
page.render derives the output format from the filename extension. The API documents PDF, PNG, JPEG, BMP, and PPM; GIF support depends on the Qt build. PNG is lossless and generally suited to UI or text. JPEG can be smaller for photographic content and supports quality options. PDF is a document output rather than a pixel-for-pixel browser screenshot.
page.render('page.png');
page.render('page.jpg', { quality: 85 });
page.render('page.pdf');
PNG compression options and JPEG quality are documented by the page.render API. Treat these as encoding choices, not a way to fix missing content or browser incompatibility.
Rank #4
Full-page capture versus a clip
| Goal | Configuration | Trade-off |
|---|---|---|
| Responsive viewport screenshot | Set page.viewportSize; leave clipRect unset |
Shows the layout at that viewport; long pages may not fit in one visible frame. |
| Specific component | Set page.clipRect to the component’s rectangle |
Stable, focused artifact, but coordinates must match the page state. |
| Printable document | Render to .pdf and configure viewport or PDF-related options documented by the API |
Useful for documents; pagination and modern CSS support are limited by PhantomJS’s old engine. |
Troubleshoot failed or incomplete captures
“Failed to load the page” or a non-success status
- Confirm the URL is reachable from the machine running PhantomJS and includes the correct scheme.
- Inspect the page’s network and TLS requirements. A certificate, redirect, DNS, or blocked resource can prevent a successful open.
- Do not immediately turn off web security or TLS checks; first correct the site or environment problem.
- Use
resourceTimeoutto bound a hanging resource, remembering that it does not signal application readiness.
The screenshot is blank or missing data
- Check that JavaScript and images are enabled before
page.open. - Increase a bounded delay or, preferably, wait for a marker that represents the loaded data.
- Log the result of
page.evaluatefor the marker and capture only after it is present. - Test the exact page: a current framework, unsupported browser API, bot check, or authentication flow may not work in PhantomJS.
The layout is the wrong size
- Set
page.viewportSizebefore opening the URL. - Check responsive breakpoints at the selected width and height.
- Use
page.clipRectonly after confirming the target coordinates in that viewport.
The file format or quality is unexpected
- Check the output extension; PhantomJS chooses the rendering format from it.
- Use PNG for lossless interface text, JPEG quality for photographic output, and PDF when a document is the intended artifact.
- Remember that GIF availability depends on the Qt build.
The capture works on one site but not another
That is expected for a suspended, archived browser engine. Compare the failing page’s JavaScript APIs, security headers, fonts, authentication, and asynchronous behavior rather than assuming a single global delay will fix it.
Reliability and operational guidance
Make each job deterministic: set the viewport and settings before opening, use a bounded readiness strategy, write to an explicit path, check the callback status, and return a nonzero exit code on failure. Record the target URL, viewport, output format, and readiness condition alongside the artifact so later differences are explainable.
A fixed delay has predictable code but uncertain completion time. A page-specific marker can reduce unnecessary waiting and avoid capturing partial content, but it requires cooperation from each application. For recurring jobs, fail loudly when the marker deadline expires instead of silently publishing an incomplete image.
Because there is no current PhantomJS support plan, validate representative pages whenever a site changes its frontend. If the requirement is modern browser fidelity rather than preserving an existing PhantomJS script, plan a migration to a maintained browser automation tool; no particular alternative is established by the PhantomJS documentation cited here.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server when you need a current, one-request capture instead of maintaining a PhantomJS runtime. It accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →One GET request returns PNG, JPEG, WebP, or PDF:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
See the ScreenshotNeo API documentation for options such as full-page capture, CSS-selector elements, dark mode, device presets, retina scale, PDF paper and page ranges, custom JavaScript and CSS, click-before-capture actions, selector hiding, network-idle waits, request blocking, headers, cookies, user agents, timezone and geolocation, transparent backgrounds, resizing, TTL caching, signed links, asynchronous webhooks, bulk capture, usage data, and the OpenAPI specification. Its parameter names are compatible with those used by other screenshot APIs, which can simplify a switch.
Best Value
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', data);
An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients, so AI agents can request captures directly. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
FAQ
Does PhantomJS wait for AJAX requests automatically?
No. Its open callback marks page-load completion, not completion of every later application request. Add a target-specific readiness check or bounded delay.
Can I capture a PDF instead of an image?
Yes. Render to a filename ending in .pdf, while remembering that pagination and modern CSS behavior come from the legacy rendering engine.
Is PhantomJS still maintained?
No. The project says development is suspended, and its repository was archived read-only on May 30, 2023; 2.1 is identified as the latest stable release.
Frequently Asked Questions
What exit code should a failed PhantomJS capture use?
Call phantom.exit(1) (or another nonzero code) after a failed page.open status or expired readiness deadline so schedulers can detect the failure.
Should I use a delay or a readiness marker?
Use a page-specific marker when you can define one; use a bounded delay only when the page’s update timing is predictable and you accept the risk of waiting too little or too long.
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.

