How to Include Background Images in PhantomJS Screenshots
Make PhantomJS screenshots include CSS background images by loading assets, matching the viewport, and waiting for asynchronous styles before rendering.
Direct answer: PhantomJS includes CSS background images when the page has loaded the image resource before page.render(). Keep page.settings.loadImages enabled (the default), set viewportSize before page.open(), verify that the background rule applies at that viewport, and wait for any page-specific asynchronous background logic before rendering.
page.render() captures the rendered page through PhantomJS’s WebKit engine, including CSS-styled HTML, SVG, images, and Canvas. The output format is normally selected from the filename extension; PNG is a good default when preserving detail. See the render API and the official screen-capture guide.
1. Minimal PhantomJS example
This script sets the viewport, leaves image loading enabled, opens the page, and renders after the page-load callback succeeds.
var page = require('webpage').create();
page.viewportSize = {
width: 1280,
height: 800
};
// loadImages defaults to true. Set it explicitly when you want
// the capture behavior to be obvious in a shared script.
page.settings.loadImages = true;
page.open('https://example.com/', function (status) {
if (status !== 'success') {
console.log('Unable to load the page.');
phantom.exit(1);
return;
}
page.render('capture.png');
phantom.exit();
});
Settings must be configured before the initial page.open() call. The viewport affects responsive CSS, so choose the same dimensions at which the background rule is defined.
2. Wait for a background that loads asynchronously
The page.open() callback tells you that navigation completed, but a site may insert or change a background later with JavaScript, a framework effect, or a lazy-loading component. Wait for the condition that proves the required background is present instead of relying on an arbitrary universal delay.
var page = require('webpage').create();
page.viewportSize = { width: 1280, height: 800 };
page.settings.loadImages = true;
function waitFor(test, onReady, timeout, interval) {
var start = new Date().getTime();
var timer = window.setInterval(function () {
var elapsed = new Date().getTime() - start;
var ready = false;
try {
ready = test();
} catch (e) {
ready = false;
}
if (ready) {
window.clearInterval(timer);
onReady();
} else if (elapsed >= timeout) {
window.clearInterval(timer);
console.log('Timed out while waiting for the background image.');
phantom.exit(1);
}
}, interval);
}
page.open('https://example.com/', function (status) {
if (status !== 'success') {
console.log('Unable to load the page.');
phantom.exit(1);
return;
}
waitFor(function () {
return page.evaluate(function () {
var element = document.querySelector('.hero');
if (!element) return false;
var style = window.getComputedStyle(element);
var background = style.backgroundImage || '';
return background !== 'none' && background.indexOf('url(') !== -1;
});
}, function () {
page.render('capture.png');
phantom.exit();
}, 15000, 100);
});
Replace .hero with the element that owns the background. For stronger validation, expose a page-specific readiness flag after your application finishes loading the asset, then poll that flag from PhantomJS.
3. Check the CSS and viewport
Responsive rules
A background can be correct at one width and absent at another because of media queries. Set page.viewportSize before navigation and inspect the rule at that exact width.
page.viewportSize = { width: 375, height: 812 };
page.open('https://example.com/', callback);
Computed style and URL
Confirm that the selected element has a computed background-image, that the URL is not relative to an unexpected stylesheet location, and that the URL is reachable from the PhantomJS process. A missing, blocked, or malformed resource cannot appear in the screenshot.
var details = page.evaluate(function () {
var element = document.querySelector('.hero');
if (!element) return { found: false };
var style = window.getComputedStyle(element);
return {
found: true,
backgroundImage: style.backgroundImage,
backgroundColor: style.backgroundColor
};
});
console.log(JSON.stringify(details));
4. Detect resource failures and timeouts
PhantomJS exposes resourceTimeout and an onResourceTimeout callback. Use them to identify slow or unreachable background URLs instead of silently producing an incomplete image.
var page = require('webpage').create();
page.viewportSize = { width: 1280, height: 800 };
page.settings.loadImages = true;
page.settings.resourceTimeout = 20000;
page.onResourceTimeout = function (request) {
console.log('Resource timeout: ' + request.url);
console.log('Error code: ' + request.errorCode);
console.log('Error string: ' + request.errorString);
};
page.onResourceError = function (resourceError) {
console.log('Resource error: ' + resourceError.url);
console.log(resourceError.errorString);
};
page.open('https://example.com/', function (status) {
if (status !== 'success') {
phantom.exit(1);
return;
}
page.render('capture.png');
phantom.exit();
});
Increasing a timeout only helps when the resource is slow. It does not fix a wrong URL, a server rejection, an unavailable certificate, or a CSS rule that never applies.
5. Control the capture format and region
With page.render(), the filename extension normally selects the format. Use PNG for sharp backgrounds, gradients, text, and transparency-sensitive detail; JPEG can reduce file size for photographic backgrounds. The screen-capture guide also documents clipRect for selecting a region.
page.clipRect = {
top: 0,
left: 0,
width: 1280,
height: 500
};
page.render('hero.png');
Choose the capture region only after confirming the background is painted. Cropping cannot restore an asset that failed to load.
6. Troubleshooting checklist
| Symptom | Likely cause | Fix |
|---|---|---|
| Background area is blank | Image URL failed or timed out | Inspect the CSS URL, add onResourceTimeout/onResourceError, and verify the URL from the capture host. |
| Background appears at desktop width but not mobile | A media query changes or removes the rule | Set viewportSize before page.open() and test the relevant breakpoint. |
| Fallback color appears instead of the image | background-image is still none when rendering |
Wait for the application’s class, style, or readiness flag before calling page.render(). |
| Only the initial hero is present | Lazy or JavaScript-driven backgrounds have not been triggered | Scroll or trigger the page behavior required by the site, then wait for the specific element/resource. |
| Capture exits with load failure | Navigation failed | Check the status argument, URL, network access, redirects, and TLS compatibility. |
| Image is visibly soft | JPEG compression or a low-resolution source | Render PNG and use a source image with enough pixels for the requested viewport. |
| Render is clipped unexpectedly | clipRect limits the output |
Remove it or set its coordinates and dimensions to the intended region. |
7. Reliability, performance, and project age
- Wait for the condition your page actually needs. A fixed sleep may be too short for a slow asset and waste time on a fast one.
- Use a bounded wait and fail clearly when the background never becomes ready. This prevents jobs from hanging indefinitely.
- Keep the viewport and capture region stable across runs so responsive CSS and output dimensions do not change unexpectedly.
- Resource diagnostics make failures observable; record the URL and timeout details with the job result.
- PhantomJS’s official home page says development is suspended and identifies QtWebKit as its backend. Modern sites may use browser features that this engine does not support, so verify compatibility before relying on it for new pages.
8. Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. Its capture pipeline accepts consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.
See the ScreenshotNeo documentation for request options.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com/ \
-o shot.webp
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com/"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js
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 failed: ${res.status}`);
const data = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', data);
ScreenshotNeo also supports full-page capture with lazy images loaded, CSS-element capture, custom CSS and JavaScript, waits for selectors, delays or network idle, request blocking, custom headers/cookies/user agents, caching with a chosen TTL, signed links, asynchronous jobs, bulk capture, usage reporting, and an MCP server with take_screenshot, get_page_info, and capture_pdf. It has 1,000 free shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
9. FAQ
Does PhantomJS need a special background-image flag?
No. Image loading is enabled by default through page.settings.loadImages. The important part is ensuring the resource and CSS rule are ready before rendering.
Can page.render() capture a background set in CSS?
Yes. PhantomJS renders CSS-styled HTML through WebKit, so a loaded CSS background can be included.
Should I always add a five-second delay?
No. The official documentation does not define a universal delay that works for every site. Wait for a page-specific condition or resource instead.
Which format should I choose?
Use PNG when preserving visual detail matters. Use JPEG when a smaller photographic image is more important. The extension normally determines the format.
Is PhantomJS suitable for a new screenshot service?
Its project page states that development is suspended. Test your target sites carefully, especially if they depend on newer browser features.


