ScreenshotNeo

BlogHow-to

How to Set Viewport Size in PhantomJS Before Taking a Screenshot

Set PhantomJS’s viewport before navigation to control responsive layout, then render after the page loads. Learn when to use clipRect and how to handle common capture issues.

By the ScreenshotNeo team4 October 20266 min read

Set page.viewportSize on the PhantomJS webpage before calling page.open(). Give it numeric width and height values. After navigation succeeds, call page.render(). The viewport controls page layout; page.clipRect controls which rectangle is rasterized into the output.

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

page.viewportSize = {
  width: 1024,
  height: 768
};

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

Replace the example dimensions with the browser window size or responsive breakpoint you need to emulate. PhantomJS is a legacy choice: its official homepage says development is suspended until further notice, so this is chiefly useful when maintaining an existing script. PhantomJS project homepage.

1. Set the viewport before opening the page

viewportSize sets the viewport used during layout, effectively simulating a browser window. Set it before navigation so the page lays itself out at the intended dimensions. Supply both properties:

page.viewportSize = {
  width: 480,
  height: 800
};

The official property reference explicitly includes both width and height. The values above are examples, not required sizes. Choose dimensions to match the layout you want to inspect—for example, a desktop window or a narrow mobile-style viewport. PhantomJS viewportSize reference.

2. Use clipRect only to control the captured bounds

The viewport and the output rectangle solve different problems:

Setting Controls Use it when
page.viewportSize The viewport used for page layout You need responsive content to lay out at a particular width and height
page.clipRect The rectangle rasterized into the image You need a fixed crop or viewport-sized output region

For a capture of the visible-sized area from the top-left, make the rectangle match the viewport:

page.viewportSize = { width: 1024, height: 768 };
page.clipRect = { top: 0, left: 0, width: 1024, height: 768 };

Changing clipRect does not set the layout viewport. If you want the whole page, do not assume that increasing viewport height alone guarantees full-page bounds; use PhantomJS’s full-page rendering behavior for your version and confirm the resulting dimensions. The official screenshot guide demonstrates the fixed viewport and matching clip rectangle. PhantomJS screen capture guide.

3. Complete script with status handling

This runnable script accepts a URL, output filename, width, and height from the command line. It sets the viewport before opening the URL, renders only after a successful open, and exits with a nonzero status on failure.

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

var url = system.args[1] || 'https://example.com/';
var output = system.args[2] || 'capture.png';
var width = parseInt(system.args[3] || '1024', 10);
var height = parseInt(system.args[4] || '768', 10);

if (!isFinite(width) || !isFinite(height) || width < 1 || height < 1) {
  console.log('Width and height must be positive integers.');
  phantom.exit(2);
}

page.viewportSize = { width: width, height: height };

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

  page.render(output);
  console.log('Saved ' + output + ' at viewport ' + width + 'x' + height);
  phantom.exit(0);
});

Run it with PhantomJS and pass the values in this order: URL, output path, width, height.

phantomjs capture.js https://example.com/ capture.png 1280 800

page.render() infers the output format from the filename extension. The official API lists PDF, PNG, JPEG, BMP, and PPM; GIF support depends on the Qt build. Use an extension matching the output you need. PhantomJS render method reference.

4. Wait for content only when the page needs it

A successful page.open() callback gives you a point to render, but sites that populate content after navigation may need additional readiness handling. The documentation shows a 200 ms timeout as an example; it is not a universal wait time. A fixed delay can still be too short for a slow page and unnecessarily long for a fast one.

For a known legacy page that needs a brief delay, place the render and exit inside a timer after a successful open:

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

  window.setTimeout(function () {
    page.render(output);
    phantom.exit(0);
  }, 200);
});

Use this only when a delay addresses a specific observed page behavior. PhantomJS documentation does not establish one site-independent readiness delay. If a page never settles or requires browser features PhantomJS does not support, increasing the delay may not fix it.

5. Troubleshooting

Symptom Likely cause Fix
The layout looks like the wrong device or breakpoint The viewport was set after navigation, or its width does not match the target layout Set page.viewportSize before page.open(); choose the intended width and height.
The output is cropped or has unexpected dimensions clipRect is setting different rasterization bounds, or the output bounds were mistaken for the layout viewport Inspect both settings. Remove clipRect if you do not need a fixed crop, or set its top, left, width, and height deliberately.
No screenshot file appears The open callback did not report success, the output path is not writable, or the script exited before rendering Check the callback status, use a writable path, and call page.render() before phantom.exit().
The screenshot is blank or missing late content The page may not have finished populating content when rendering occurs, or it may not work correctly in PhantomJS Confirm the open status; add a targeted wait for the page behavior you observe. Do not rely on an arbitrary delay as a universal fix.
The file format is wrong The filename extension selects the render format Use an extension such as .png, .jpg, or .pdf that matches the desired format and build support.
The script cannot reproduce a current site PhantomJS development is suspended, and the documentation does not establish compatibility with current browser behavior For a maintained script, assess a currently supported browser-based capture approach or use a screenshot API.

6. Performance, reliability, and cost

  • Performance: Choose the smallest viewport that answers the layout question, and avoid waiting longer than the page behavior requires. A larger viewport can change responsive layout and output dimensions; it is not a substitute for a readiness condition.
  • Reliability: Check the open status and only render on success. If your script depends on a particular site, validate that site’s behavior in the PhantomJS version and Qt build you run. The project’s suspended development status means its documentation should not be read as a promise of compatibility with current sites.
  • Cost: PhantomJS itself is a software workflow; the cited documentation provides no service pricing or benchmark. Your practical costs are the compute and maintenance required to run a legacy browser script. Avoid treating the documentation’s example dimensions or 200 ms delay as performance guarantees.

7. Or skip the browser setup

If you need a screenshot without managing PhantomJS, ScreenshotNeo provides a website screenshot API and MCP server. Its API accepts one GET request with a URL and returns an image or PDF. The relevant options and examples are in the ScreenshotNeo API documentation.

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

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
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(`Screenshot request failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

Set the desired viewport using the API’s viewport options in the documentation. ScreenshotNeo also removes cookie banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots, and the free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.

FAQ

Does viewportSize resize the screenshot file?

It sets the viewport used for layout. The captured bounds can also depend on rendering behavior and clipRect.

Do I need clipRect for a normal viewport-sized screenshot?

Not necessarily. Use it when you need explicit capture bounds; keep it aligned with the viewport for a matching visible-area rectangle.

Can I use a mobile width?

Yes. Set the width and height to the dimensions you want PhantomJS to use for layout. This sets viewport dimensions; it does not, by itself, document a device-specific user agent or device emulation.

Is PhantomJS suitable for a new screenshot service?

The project homepage says development is suspended. That makes it primarily relevant to maintaining existing scripts; check the browser requirements of your target sites before relying on it.