PhantomJS Screenshot Is Cut Off: How to Capture the Whole Page
Fix a cut-off PhantomJS screenshot by checking clipRect, viewportSize, page readiness, and output sizing. Includes a diagnostic script and a modern API option.
If a PhantomJS screenshot stops partway down the page, check page.clipRect first. It sets the region included in the capture, so a fixed rectangle can crop the rest of the page. page.viewportSize controls the browser window used for layout; it does not by itself remove a clipping boundary. Set the viewport before opening the page, remove an unintended clip rectangle, and render only after the page has produced the content you need.
This is legacy troubleshooting: the PhantomJS project says development is suspended until further notice and identifies QtWebKit as its backend. The cause of a particular crop still depends on the script, page, PhantomJS version, and output. PhantomJS project site.
1. Check the capture boundary: clipRect
Search your script for page.clipRect. PhantomJS documents it as the portion of the page included in a screenshot. A rectangle with a fixed width and height limits capture to that region. Remove the assignment while diagnosing an unintended crop, or enlarge it when you deliberately want a bounded capture.
// Remove or comment out a restrictive assignment such as:
// page.clipRect = { top: 0, left: 0, width: 1024, height: 768 };
// Keep a clip rectangle only when you want to capture a specific region.
page.clipRect = { top: 0, left: 0, width: 1280, height: 2400 };
The dimensions above illustrate configuration; they are not a recommended size for every page. A tall rectangle may still be insufficient for a longer document. PhantomJS’s screen capture guide shows a 1024-by-768 clip rectangle as an example of the captured portion.
2. Set viewportSize for the page layout
page.viewportSize sets the simulated browser window dimensions used for layout. It can change responsive breakpoints and the arrangement of content, but it is a different control from clipRect. Set both width and height before calling page.open, as in the official viewportSize API example.
page.viewportSize = { width: 1280, height: 800 };
If a page switches to a mobile layout at a narrow width, increasing the viewport width may change its layout. It will not fix a short fixed clip rectangle. Conversely, removing clipRect does not select the layout width you want; set the viewport explicitly.
3. Runnable diagnostic script
Save this as capture.js and run it with an installed PhantomJS executable, for example phantomjs capture.js https://example.com/ page.png. It checks the page-open status, sets the viewport before navigation, leaves clipRect unset, waits briefly after a successful load, and renders according to the output filename extension.
var page = require('webpage').create();
var system = require('system');
var url = system.args[1] || 'https://example.com/';
var output = system.args[2] || 'page.png';
page.viewportSize = { width: 1280, height: 800 };
// Do not set page.clipRect when diagnosing an unintended crop.
page.open(url, function (status) {
if (status !== 'success') {
console.log('Unable to load the address: ' + url);
phantom.exit(1);
return;
}
// Illustrative delay only. Choose readiness criteria for the target page.
window.setTimeout(function () {
page.render(output);
phantom.exit(0);
}, 200);
});
The 200 millisecond delay mirrors a short illustrative wait in PhantomJS documentation; it is not a universal readiness guarantee. A site may need longer for scripts, images, or asynchronous content to appear. A successful page.open callback also does not prove that every late-loading element is ready.
4. Wait for the content you need
Render after the page has loaded and after important asynchronous content is available. Inspect the rendered DOM and the site’s behavior to determine what needs to finish before capture. If a fixed delay is used, treat it as page-specific and allow enough time for the target’s work. The official examples use a short delay after a successful load, but do not establish a delay that works for all sites.
Lazy-loaded content needs particular care: a fixed wait may not cause below-viewport content to load. The PhantomJS sources used for this guide do not document a universal full-page lazy-load procedure. Check whether the missing content exists in the DOM, whether the page loads it only after scrolling, and whether the site has finished rendering before page.render. Do not assume a taller clip rectangle alone will trigger lazy loading.
5. Choose the right output and sizing controls
- Raster images: Use a filename extension such as
.pngor.jpg;page.render(filename)infers the output format from the extension. - Supported formats: The render API lists PDF, PNG, JPEG, BMP, and PPM. GIF depends on the Qt build. See page.render.
- PDF sizing: Configure
page.paperSizewhen PDF page dimensions, orientation, or margins are wrong. It supports explicit width and height or formats such as A4 and Letter, with optional orientation and margins. This controls PDF page layout; it is distinct from the screenshot crop settings. See paperSize.
// PDF example: configure paper size before rendering.
page.paperSize = {
format: 'A4',
orientation: 'portrait',
margin: '1cm'
};
page.render('page.pdf');
For an image that appears cut off, first inspect clipRect and page readiness. For a PDF with unexpected page dimensions, inspect paperSize as well.
6. Troubleshooting checklist
| Symptom | Likely cause | What to check or change |
|---|---|---|
| Image ends at a clean rectangular boundary | A fixed page.clipRect |
Remove it during diagnosis or increase its width and height to cover the desired region. |
| Page layout differs from the normal browser | Viewport dimensions trigger a different responsive layout | Set page.viewportSize before page.open; include width and height. |
| Content appears after the screenshot is taken | Rendering happened before scripts, images, or asynchronous updates finished | Wait for the relevant content or readiness condition. A short example delay is not a universal solution. |
| Lower sections are missing even with a taller capture area | Content may be lazy-loaded or absent from the DOM at render time | Inspect the DOM and page behavior. Determine whether content loads after scrolling; the cited PhantomJS docs do not provide a universal lazy-load procedure. |
| Output file is not the expected image type | The extension selects the render format | Use the intended extension, such as .png or .jpg, and check the format support for the Qt build. |
| PDF has unexpected page dimensions or margins | paperSize is unset or configured for different output |
Set its format or width and height, orientation, and margins for PDF output. |
| Script prints that the address could not be loaded | page.open did not report success |
Check the URL and whether the target is reachable from the machine running PhantomJS before investigating crop settings. |
7. Performance, reliability, and cost
For the existing PhantomJS script, the main reliability tradeoff is readiness: rendering immediately can miss late content, while an arbitrary long delay adds time without guaranteeing that the page is ready. Wait for the content relevant to your capture, and treat any fixed delay as a target-specific choice. A large viewport affects page layout; a large clipRect expands the selected capture region. Choose dimensions based on the output you need.
PhantomJS development is suspended, so it is a legacy option rather than a project with active development according to its own homepage. The documentation reviewed does not establish current compatibility with modern websites, a universal full-page lazy-load method, or a general performance benchmark. There is no ScreenshotNeo charge for using this local PhantomJS diagnostic script.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. Make one GET request with a URL to receive an image or PDF; its documentation covers the API options.
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()));
- Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are never billed. Responses identify the page verdict and billing status in headers.
- An MCP server lets AI agents use
take_screenshot,get_page_info, andcapture_pdf. - The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots. Every feature is available on every plan.
Sign up for 1,000 free screenshots a month, with no card.
FAQ
Does increasing viewportSize.height guarantee a full-page screenshot?
No. It sets the simulated browser window for layout. Check for a limiting clipRect and ensure the page has produced the content you want to capture.
Should I use clipRect at all?
Use it when you intentionally want a bounded region. Leave it unset while diagnosing a screenshot that stops unexpectedly.
Is the 200 millisecond delay enough?
Not necessarily. It is an example delay in the documentation, not a guarantee for any particular site.
Is PhantomJS still actively developed?
The PhantomJS homepage says development is suspended until further notice.


