Recommended Free Tools
Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
To save a PhantomJS page after JavaScript has populated it, do not render as soon as navigation finishes. Check that page.open succeeded, wait for a page-specific signal that the data you need is present, and only then call page.render. PhantomJS runs page JavaScript by default, but its load callback does not guarantee that an application’s later asynchronous updates have finished.
Why PhantomJS needs an explicit readiness check
PhantomJS’s page.open(url, callback) callback reports whether the page load succeeded. It is a useful first check, not a universal signal that every item on the page is ready. A site may populate results after load through a timer or asynchronous request. Rendering immediately can therefore produce an image or PDF with a spinner, empty results, or only part of the page.
The reliable sequence is: configure the page, open the URL, check the open status, wait for a condition that represents the data you want, and render. The condition must be specific to the target page. For a search result, for example, it might mean that a result element exists and contains non-empty text. A generic test such as “the document is complete” may not detect data that is still being fetched.
Save a dynamic page with a bounded, page-specific wait
The following PhantomJS script polls for a selector whose text is non-empty. Replace the URL, selector, and output filename with values for your page. The selector is intentionally an example: it must identify the content that matters on the site you are capturing.
#1 Best Overall
var webpage = require('webpage');
var system = require('system');
var url = system.args[1] || 'https://example.com';
var selector = system.args[2] || '#results';
var output = system.args[3] || 'capture.png';
var maxWaitMs = 15000;
var pollEveryMs = 250;
var page = webpage.create();
page.viewportSize = { width: 1365, height: 900 };
page.settings.javascriptEnabled = true;
page.settings.resourceTimeout = 10000;
page.onResourceTimeout = function (request) {
console.log('Resource timed out: ' + request.url);
};
page.open(url, function (status) {
if (status !== 'success') {
console.log('Unable to load page: ' + status);
phantom.exit(1);
return;
}
var elapsed = 0;
var timer = setInterval(function () {
var ready = page.evaluate(function (selector) {
var element = document.querySelector(selector);
return !!(element && element.textContent && element.textContent.trim());
}, selector);
if (ready) {
clearInterval(timer);
page.render(output);
console.log('Saved ' + output);
phantom.exit(0);
return;
}
elapsed += pollEveryMs;
if (elapsed >= maxWaitMs) {
clearInterval(timer);
console.log('Timed out waiting for data in selector: ' + selector);
phantom.exit(2);
}
}, pollEveryMs);
});
Run it with arguments for the target page, readiness selector, and output path, for example:
phantomjs save-page.js 'https://example.com/search' '#results' 'results.png'
This script returns a non-zero exit code if navigation fails or the expected data does not appear before the bounded wait expires. It checks for non-empty text; adapt the predicate if readiness is represented by an attribute, a count of result cards, or an application-specific state instead. For instance, a page may contain a “No results” message that is still a valid completed state; the condition should distinguish that outcome from a still-loading page.
Make the readiness test reflect the page
- Wait for a particular result: check for the expected result container or a known element within it.
- Wait for a completed empty result: accept either result entries or the page’s explicit empty-state message.
- Wait for a loading indicator to disappear: verify the indicator is absent and, ideally, that the data area is in its final state. Disappearance alone may also happen after an error.
- Wait for a known application state: inspect a data attribute or other stable signal if the page exposes one.
Keep the wait finite. If the expected signal never appears because the site changed, the selector is wrong, or a request failed, an unbounded polling loop can leave the process running indefinitely. The sample reports the timeout rather than silently saving an incomplete file.
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #2
Choose an output format and capture area
page.render(filename) writes the rendered page to the filename you supply. The extension selects the output format. PhantomJS’s render API documents PDF, PNG, JPEG, BMP, PPM, and GIF where supported by the Qt build; availability can therefore depend on how PhantomJS was built. Use an extension and format appropriate to your installed build.
- PNG: a lossless image option for screenshots where sharp text or interface details matter.
- JPEG: a compressed image option when a smaller photographic image is acceptable.
- PDF: use a
.pdffilename when you need a document rather than a raster image. Check the resulting pagination and layout for the page you are saving.
Set page.viewportSize before navigation when the page’s responsive layout should be rendered at a chosen viewport size. Use page.clipRect when you need to constrain the capture to a particular rectangle. These controls affect what is rendered; they do not make late-arriving application data ready. Decide the viewport and clipping area independently from the readiness condition.
Configure settings before opening the page
PhantomJS’s page settings apply to the initial page.open. Configure them before navigation; changing a setting after the page has opened does not change the settings for that load. JavaScript is enabled by default, but setting page.settings.javascriptEnabled = true explicitly can make the script’s intent clear. If the target requires JavaScript, disabling it will prevent the page from running its normal client-side code.
The example sets resourceTimeout to 10,000 milliseconds as an optional bound on individual resource loads. A resource timeout can help with a stalled request, but it is not a signal that the data you need has loaded. Logging the timed-out resource helps identify a possible cause; continue to base rendering on the page-specific condition.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Fixed delay versus checking the data
A fixed delay is simple, but it makes a poor default readiness test. If the delay is too short, rendering can happen before the data appears; if it is longer than necessary, every capture waits unnecessarily. A bounded poll for the expected element or state is generally more robust because it can proceed as soon as that condition is met and stop after a maximum wait.
A delay can still be a fallback when the page offers no observable readiness signal. In that case, choose a finite wait, understand that it cannot prove the application finished, and inspect the resulting capture. For repeatable work, look for a stable page-specific DOM element or state before relying on elapsed time alone.
Rank #4
Troubleshoot missing, partial, or incorrectly sized captures
The output is blank or has no dynamic data
- Check that the
page.opencallback receivedsuccess; do not render after a failed navigation. - Confirm JavaScript is enabled in settings before opening the page.
- Check that the selector and readiness predicate identify the actual data-bearing element. A selector that matches an empty container is not enough if the content appears later.
- Move
page.renderso it runs only after the readiness condition passes.
The script times out waiting for data
- Inspect the selector against the page’s actual DOM and replace it if the site uses a different element.
- Check whether the page has an error state or an explicit empty-results state that your predicate should handle.
- Look at resource-timeout messages for requests that did not complete. A timeout on one resource does not establish whether the required data was received.
- Increase the bounded wait only if the page’s expected data genuinely takes longer; do not remove the bound and risk an endless run.
The capture is cropped or uses the wrong layout
Set viewportSize before opening the page to control its viewport and responsive layout. If only a region is needed, adjust clipRect. Check that the rectangle covers the intended content and that the page has reached the desired layout before rendering.
A script loaded with includeJs is missing
If you use PhantomJS’s includeJs helper to load a script into the page, do not call phantom.exit() before its callback runs. The automation guide warns that the exit call belongs inside the include callback so the process does not quit before the script has loaded. Any readiness test dependent on that script must happen after it is available.
Know the limits of PhantomJS for current websites
PhantomJS is legacy software. The upstream project repository says, “Important: PhantomJS development is suspended until further notice.” The repository is archived and read-only as of May 30, 2023, and its README identifies version 2.1 as the latest stable release. Those project statements do not establish that PhantomJS is suitable for modern websites. Pages that depend on newer browser features may behave incorrectly, so compatibility is a risk to evaluate for your particular page rather than a universal guarantee of failure.
Best Value
If you maintain an existing PhantomJS workflow, a page-specific readiness check can prevent captures from racing the page’s own asynchronous updates. It cannot add browser capabilities that PhantomJS lacks or guarantee that an unsupported site will render faithfully.
Or skip the browser setup
For a screenshot without writing and maintaining a PhantomJS script, ScreenshotNeo accepts one GET request with a URL and returns a screenshot or PDF. See the ScreenshotNeo API documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and responses include X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents and MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Sign up for ScreenshotNeo’s free plan: 1,000 screenshots a month, no card required.
Frequently Asked Questions
Does PhantomJS run JavaScript when opening a page?
Yes. JavaScript is enabled by default; configure page settings before the initial page.open call if you need to change that behavior.
Can page.open success guarantee that all dynamic data has loaded?
No. It reports page-load completion and success or failure, but an application may populate data afterward. Wait for a meaningful page-specific condition before rendering.
Which output formats can page.render save?
The API lists PDF, PNG, JPEG, BMP, PPM, and GIF when supported by the installed Qt build. The filename extension selects the requested format.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →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.

