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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

“Save a webpage” can mean three different outputs: a rendered image or PDF, the JavaScript-generated HTML currently in the DOM, or a remote file such as an image or stylesheet. CasperJS and PhantomJS use different calls for each job. Use CasperJS capture() or PhantomJS page.render() for a visual file, CasperJS captureSelector() for one element, getHTML() for rendered markup, and download() for a resource URL. The examples below show the documented workflow, but both projects are legacy software: PhantomJS development is suspended and CasperJS is no longer actively maintained.

Choose the artifact before writing code

What you need Use Result
Whole-page visual CasperJS capture() or PhantomJS page.render() PNG, JPEG, GIF, or PDF, depending on the renderer and options
One visible region CasperJS captureSelector() An image of the element matched by a CSS selector
JavaScript-rendered markup CasperJS getHTML() A string containing the current DOM HTML
Static remote file CasperJS download() A downloaded resource, not the post-JavaScript DOM

That distinction matters. A screenshot is pixels after rendering; HTML is text returned by the page; a download retrieves a remote resource. The CasperJS API documents these as separate operations (CasperJS API).

Save a rendered page with CasperJS

CasperJS provides navigation and step sequencing while PhantomJS performs the underlying rendering. Start a Casper instance, open the URL, capture after navigation, and call run() to execute the queued steps. This is the short documented pattern:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
var casper = require('casper').create();

casper.start('https://example.com/', function() {
    this.capture('page.png');
});

casper.run();

The capture belongs inside a Casper step so it runs after the page-opening step. In a real page, “opened” does not always mean that every asynchronous widget has finished. Add an explicit wait when the page needs more time, or wait for a selector that proves the relevant content exists.

Capture a selected element

Use captureSelector() when a full viewport image is unnecessary:

var casper = require('casper').create();

casper.start('https://example.com/', function() {
    this.captureSelector('main.png', 'main');
});

casper.run();

The selector is CSS. If it matches nothing, you should treat that as a failed capture rather than silently accepting an empty result; add a wait or an existence check appropriate to your CasperJS version.

Control format, quality, and clipping

capture(filepath, clipRect, imgOptions) proxies PhantomJS rendering and accepts a temporary clipping rectangle plus image options. CasperJS documents an explicit image format and a quality setting from 1 to 100. Quality is an encoding option, not a performance or visual benchmark. Use clipping when you need a fixed rectangle; it does not automatically discover the full height of an arbitrarily long document.

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

Direct PhantomJS rendering

For lower-level control, create a PhantomJS webpage, open the address, check the callback status, and render only after a successful open. The official screen-capture example is:

var page = require('webpage').create();
page.open('https://example.com/', function(status) {
  if (status === 'success') {
    page.render('page.png');
  }
  phantom.exit();
});

PhantomJS documents PNG, JPEG, GIF, and PDF output. Set viewportSize before opening to control the browser viewport:

var page = require('webpage').create();
page.viewportSize = { width: 1440, height: 900 };
page.open('https://example.com/', function(status) {
  if (status === 'success') {
    page.render('page.pdf');
  }
  phantom.exit();
});

A viewport is the simulated browser window. A clipRect is the rectangle actually written to the file. Neither setting alone means “capture the entire, infinitely tall page.” Use the documented rectangle deliberately, and verify the resulting dimensions for your use case. See PhantomJS screen capture.

PDF output considerations

PDF rendering uses the same page.render() call, but pagination and paper layout are renderer concerns rather than HTML extraction. If you need a print-ready document, inspect page breaks, margins, and content that appears only after scripts run. The official guide establishes that PDF is a supported render format; it does not provide a current compatibility guarantee for modern sites.

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

Retrieve the JavaScript-rendered HTML

When your goal is markup rather than pixels, call getHTML() after the page has reached the state you want:

var casper = require('casper').create();

casper.start('https://example.com/', function() {
    var html = this.getHTML();
    require('fs').write('rendered.html', html, 'w');
});

casper.run();

getHTML() returns a string, so writing it to a file is a separate action. Narrow the result to an element with getHTML('main'). The documented outer option can include the selected node itself when you need its opening and closing tag, rather than only its children. This is the appropriate CasperJS method for DOM produced or changed by page JavaScript.

Do not use download() for rendered DOM

download(url, target, ...) is intended for a remote resource such as a static file. It fetches that URL; it does not ask the browser for the live DOM after scripts, user interactions, or client-side rendering. Use getHTML() for the latter.

Make captures deterministic

  • Set the viewport first: choose width and height before navigation so responsive breakpoints are predictable.
  • Wait for evidence: wait for a selector or a known delay when content is inserted asynchronously.
  • Capture after interaction: perform clicks or other steps before calling capture() if the desired state is hidden behind a control.
  • Use a fixed clip: provide clipRect for a repeatable region; do not confuse it with full-page scrolling.
  • Check status and files: only render after a successful page.open() status and verify that the output exists and has non-zero size.
  • Keep HTML and images separate: save getHTML() as text and render screenshots or PDFs as binary output.

These practices improve repeatability, but they cannot turn an unmaintained browser engine into a current-browser compatibility test.

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

Common failures and fixes

The output is blank or missing

Cause: rendering ran before a successful open, or the page failed to load. Fix: check the status callback in PhantomJS, keep phantom.exit() after the callback, and log navigation errors. In CasperJS, place capture in a post-navigation step and add a wait for required content.

The screenshot contains only the top of the page

Cause: the viewport or clip rectangle covers only the visible window. Fix: define the rectangle you actually need and, for a long document, use the renderer’s documented full-page strategy rather than assuming a viewport captures all scrollable content.

A selector capture is empty

Cause: the selector did not match at capture time, often because a client-side component had not rendered. Fix: wait for the selector, confirm the selector spelling, and capture after the step that creates the element.

HTML is the original source, not the live page

Cause: a resource download or an external HTTP fetch was used instead of querying the browser DOM. Fix: call getHTML() after scripts finish; use its selector and outer arguments when you need a specific node.

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.

Modern sites fail or display incorrectly

Cause: PhantomJS and CasperJS are legacy tools. The PhantomJS homepage states, “Important: PhantomJS development is suspended until further notice” (PhantomJS project), and the CasperJS repository describes the project as “no longer actively maintained” (CasperJS repository). The official material does not establish a current operating-system or website compatibility matrix. Treat this workflow as maintenance of a legacy environment, not as evidence that current JavaScript applications will render correctly.

The script hangs

Cause: a page, resource, or callback never reaches the expected state. Add bounded waits in your surrounding process, reduce unnecessary page work, and log each navigation step. Avoid declaring success solely because the process is still running.

Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server for developers. One GET request returns PNG, JPEG, WebP, or PDF and avoids installing CasperJS or PhantomJS. Before capture it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and whether the shot was billed.

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)

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}`);

See the ScreenshotNeo documentation for the full parameter set. It supports full-page lazy-image loading, CSS-selector element capture, dark mode, 12 device presets or custom viewports, retina scale, PDF paper and page ranges, HTML/CSS to image, custom JavaScript and CSS, clicks, selector or network-idle waits, ad/tracker/request blocking, headers, cookies, user agents, Authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, OpenAPI, and compatible parameter names used by other screenshot APIs. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

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

The Free plan includes 1,000 screenshots each month with no card. Paid plans start at $5 for 3,000 shots; yearly billing gives two months free. Create a free ScreenshotNeo account to try it.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Cost, reliability, and maintenance decisions

CasperJS and PhantomJS do not charge per capture, but you own the environment, browser process, file storage, waits, retries, and compatibility risk. A successful callback only reports the legacy engine’s result; it is not a guarantee that a modern site’s content is complete. For repeatable archival work, record the URL, viewport, clip rectangle, output format, timestamp, and any waits or interactions used.

An API shifts browser operations to a service and can report billing and page verdicts in headers. ScreenshotNeo’s paid plans begin at $5 for 3,000 screenshots, while failed loads and other listed non-clean outcomes are not billed. Choose the legacy scripts when you must preserve an existing CasperJS/PhantomJS workflow or need their exact local behavior; choose an API when you want a current, remotely managed capture endpoint or AI-agent access.

FAQ

Can CasperJS save a webpage as HTML?

Yes. Call getHTML() after the page reaches the desired rendered state, then write the returned string to a file.

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

Does PhantomJS capture PDF files?

Its official screen-capture documentation lists PDF along with PNG, JPEG, and GIF output through page.render().

Is CasperJS suitable for a new project?

The project is described as no longer actively maintained, while PhantomJS development is suspended. The documented workflow is therefore best understood as legacy maintenance.

What is the difference between viewportSize and clipRect?

viewportSize sets the simulated browser window; clipRect limits the rectangle written to the capture. They control different stages of rendering.

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.