ScreenshotNeo

BlogHow-to

How to Take Full-Page Screenshots with PhantomJS

Learn the documented PhantomJS method for full-page screenshots, including viewport setup, dynamic pages, formats, errors, and a modern API option.

By the ScreenshotNeo team29 September 20268 min read

How to Take Full-Page Screenshots with PhantomJS

Direct answer: create a PhantomJS webpage, set page.viewportSize before opening the URL, call page.open(), check that the status is success, and then call page.render('full-page.png') without setting clipRect. According to the PhantomJS API, omitting clipRect makes page.render() process the entire page rather than only the visible viewport.

This distinction matters: viewportSize controls the layout that the page sees, while clipRect limits the area rasterized into the output. PhantomJS development is suspended until further notice, so treat this as a legacy workflow for existing scripts rather than a recommendation for new production systems. The official project notice is on the PhantomJS homepage.

1. The minimal full-page PhantomJS script

Save this as full-page.js:

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

// This controls responsive layout, not the final crop.
page.viewportSize = {
  width: 1024,
  height: 768
};

page.open('https://example.com/', function (status) {
  if (status !== 'success') {
    console.log('Unable to load the address!');
    phantom.exit(1);
    return;
  }

  // No clipRect: render the complete page.
  page.render('full-page.png');
  phantom.exit();
});

Run it with the documented command-line pattern:

phantomjs full-page.js

The output format is inferred from the filename extension. A .png file is the conventional lossless screenshot. The same method can write JPEG, BMP, PPM, GIF where supported by the build, or PDF output. The render API documents the supported formats and the quality parameter.

2. Why omitting clipRect captures the whole page

PhantomJS has two separate concepts that are often confused:

A full-page render uses the viewport for layout while the absence of clipRect allows the complete document to be captured.
A full-page render uses the viewport for layout while the absence of clipRect allows the complete document to be captured.
Setting What it controls Full-page recommendation
page.viewportSize The browser viewport used for responsive layout and media queries. Set the width and height you want the page to use.
page.clipRect The rectangular region rendered to the file. Leave it unset when you want the entire page.
page.render() Rasterizes the page or the configured clipping rectangle. Call it after the page is ready.

The clipRect documentation defines top, left, width, and height. If you set a rectangle, only that region is rendered. For example, this intentionally creates a bounded 800 by 600 image:

page.clipRect = {
  top: 0,
  left: 0,
  width: 800,
  height: 600
};
page.render('viewport.png');

Remove that assignment for a full-page capture. Increasing the viewport height is not a substitute for removing the clip rectangle; it changes layout and may still leave you with a bounded image.

3. A safer script with readiness and diagnostics

The basic example checks only the load status. For a page you control, add a site-specific readiness condition before rendering. A fixed delay can help with a known animation or late script, but the documentation’s 200 ms example is not a universal guarantee that images, lazy content, or asynchronous requests have finished.

var system = require('system');
var webpage = require('webpage');

var url = system.args[1] || 'https://example.com/';
var output = system.args[2] || 'full-page.png';
var page = webpage.create();

page.viewportSize = { width: 1366, height: 900 };
page.settings.userAgent = 'PhantomJS full-page capture';
page.settings.resourceTimeout = 30000;

page.onError = function (message, trace) {
  console.log('Page error: ' + message);
  trace.forEach(function (item) {
    console.log('  at ' + item.file + ':' + item.line);
  });
};

page.onResourceError = function (resourceError) {
  console.log('Resource error: ' + resourceError.url);
};

page.open(url, function (status) {
  if (status !== 'success') {
    console.log('Unable to load ' + url + ' (status: ' + status + ')');
    phantom.exit(1);
    return;
  }

  // Replace this delay with a page-specific readiness check when possible.
  window.setTimeout(function () {
    page.render(output);
    console.log('Saved ' + output);
    phantom.exit(0);
  }, 200);
});

Pass a different target and output path like this:

phantomjs full-page.js https://example.com/article article.png

For a page you own, a stronger pattern is to expose a marker such as window.__SCREENSHOT_READY__ = true after your application has loaded its data, then poll that value from PhantomJS before calling render. This is an implementation recommendation based on the limits of a fixed delay; PhantomJS documentation does not define one universal “all content loaded” condition.

4. Viewport choices and responsive layouts

Choose the viewport for the layout you want to document:

  • Desktop: 1280–1440 pixels wide is useful for desktop breakpoints.
  • Tablet: use the width of the breakpoint you need to verify.
  • Mobile: set a narrow width and include the required viewport height.

The API requires viewport height as well as width. The same page can produce different full-page images at different widths because responsive CSS changes line wrapping, navigation, columns, and total page height. Record the viewport settings beside generated screenshots so later comparisons remain meaningful.

5. Formats, quality, and output files

Use the extension to select the format:

Extension Use Trade-off
.png Documentation, pixel comparison, text and UI. Lossless; larger files are common.
.jpg or .jpeg Photographic pages or smaller previews. Lossy compression can introduce artifacts.
.pdf Document-style page output. Not a conventional raster screenshot.
.bmp, .ppm, .gif Specialized workflows where the build supports them. Check the exact legacy build before depending on them.

The render API describes quality as an integer from 0 to 100. For JPEG, it controls visual quality. For PNG, changing compression affects file size rather than image quality. Example:

page.render('page.jpg', { format: 'jpg', quality: 85 });

When portability matters, prefer the filename-extension behavior shown in the official examples and verify format support in the PhantomJS binary installed on your system.

6. Dynamic content, lazy images, and animations

page.open reporting success means the navigation completed according to PhantomJS; it does not prove that every later XHR, image, animation, or lazy-loaded section is complete. Common strategies are:

  1. Set a readiness flag in the application you control.
  2. Poll for a selector that appears only after the important content is present.
  3. Use a short, known delay for a specific animation or transition.
  4. Disable animations in page CSS when you need deterministic visual output.
  5. Scroll through pages that lazy-load content, triggering the loads before rendering.

PhantomJS is an old QtWebKit browser. Modern JavaScript syntax, browser APIs, TLS behavior, and bot protection can prevent a page from rendering correctly. Do not assume that a script that works on a simple static page will work on a current single-page application.

7. Troubleshooting common failures

Symptom Likely cause Fix
Only the visible viewport is saved A clipRect was set. Remove page.clipRect; keep viewport settings for layout only.
“Unable to load the address!” DNS, TLS, network, redirect, or server failure. Check the URL from the same host, inspect resource errors, and verify the exit status.
Blank or partly blank image Rendering happened before application content was ready. Use a page-specific readiness marker or selector and render afterward.
Images are missing Lazy loading, blocked resources, or a request that finishes after render. Trigger lazy loads, inspect onResourceError, and wait for the required assets.
Script never exits phantom.exit() was not called on every path. Exit on both success and failure, as in the examples.
Text or layout differs from a normal browser Different viewport, user agent, fonts, or unsupported browser features. Set viewport and user agent deliberately; install required fonts; expect legacy compatibility limits.
Output format fails The installed Qt build lacks support for that format. Use PNG first, then verify the exact build’s supported formats.
Long pages consume excessive memory A very tall raster surface and large images. Capture sections with clip rectangles, reduce viewport width, or move to a maintained browser/API workflow.

8. Performance, reliability, and cost considerations

Full-page rendering cost is mainly local CPU, memory, network time, and output storage. Very tall pages create large raster surfaces; PNG encoding can also take time. Keep the viewport no wider than necessary, avoid capturing hidden pages, and write output to a location with enough disk space.

For repeatable builds, pin the PhantomJS binary and fonts, use a fixed viewport, and capture pages from a controlled environment. Log the URL, status, elapsed time, output path, and resource errors. Retry only failures that are plausibly transient; repeated retries will not fix unsupported JavaScript or a permanently blocked resource.

PhantomJS itself does not provide a hosted per-screenshot price. Your operational cost is the machine and maintenance burden. Since development is suspended, evaluate the compatibility risk before making it a new dependency.

9. Or skip the browser setup

If you need reliable screenshots without installing and maintaining a legacy headless browser, ScreenshotNeo provides a GET-based screenshot API. See the ScreenshotNeo documentation for the full parameter list.

A hosted capture service can remove common consent and overlay elements before saving the screenshot.
A hosted capture service can remove common consent and overlay elements before saving the screenshot.

One request returns a PNG, JPEG, WebP, or PDF:

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

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://stripe.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. It also offers an MCP server so Claude, Cursor, and other MCP clients can call take_screenshot, get_page_info, and capture_pdf. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account.

10. Practical checklist

  • Set viewportSize before page.open.
  • Leave clipRect unset for a full-page image.
  • Check the page.open status before rendering.
  • Wait for a page-specific readiness condition when content is dynamic.
  • Call phantom.exit() on success and failure.
  • Choose PNG, JPEG, or PDF based on the required output.
  • Log resource errors and keep the PhantomJS binary and fonts consistent.
  • Plan for compatibility limits because PhantomJS development is suspended.

11. Frequently asked questions

Does page.render() automatically capture below the fold?

Yes, when no clipping rectangle is configured, the documented behavior is to process the entire page. The viewport still controls layout.

Should I set the viewport height to the page height?

No. Set the viewport dimensions needed for layout. A full-page render does not require manually measuring the document height.

Is a 200 ms delay enough?

Not generally. It is an example from the documentation, not a guarantee for asynchronous content. Use a readiness signal you control whenever possible.

Can PhantomJS capture modern websites?

Sometimes, but compatibility is not assured. PhantomJS uses an old QtWebKit engine and its development is suspended, so current frameworks and browser features may fail.

Can I capture only one section?

Yes. Set page.clipRect with the desired top, left, width, and height, then call render. That produces a bounded capture rather than a full-page one.

Which output should I use for visual regression tests?

PNG is usually the safest choice because it is lossless. Keep viewport, fonts, timing, and browser version fixed to reduce unrelated differences.

What is the simplest hosted alternative?

ScreenshotNeo’s API accepts one GET request and returns an image or PDF. It also removes common consent UI and reports whether a response was billed.