Features Full Page Screenshot Wait for Selector & Delay Block Cookie Banners Custom Viewport & Device Website to PDF HTML to Image Dark Mode Image Format & Quality MCP Server Webhook Pricing Docs Blog Log In Sign Up

PhantomJS Alternative

Vitalii Holben Vitalii Holben

Most developers searching for a PhantomJS alternative did not choose PhantomJS themselves. It came with the codebase: baked into a CI pipeline, wired into a PDF generator, or buried in an old test suite. Then an OS upgrade broke the binary, or a security audit flagged it red, and now you need a working replacement. This page covers the three realistic options in 2026: Puppeteer, Playwright, and a managed screenshot API, with migration code and honest trade-offs.

What PhantomJS was and why it stopped

PhantomJS launched in 2010 as the first practical headless browser. Before it existed, taking a programmatic screenshot of a web page was painful. PhantomJS made it simple: one binary that could load pages, run JavaScript, and export screenshots or PDFs. It became the foundation for CasperJS, hundreds of testing setups, and more PDF pipelines than anyone can count.

Then Chrome 59 shipped headless mode in June 2017. A real, actively maintained browser engine could now run without a GUI. PhantomJS's maintainer, Vitaly Slobodin, saw the writing on the wall and stepped down the next year. His message was direct: headless Chrome is faster and more stable than PhantomJS. The project was suspended in March 2018, and the GitHub repository was archived shortly after.

That was over eight years ago. Since then the web picked up CSS Grid, Flexbox, modern JavaScript, single-page apps, web fonts, and a dozen other features that PhantomJS will never understand.

What actually breaks when you run PhantomJS today?

PhantomJS renders pages using a QtWebKit engine from roughly 2013. Anything built with modern tools looks wrong or shows up blank.

Feature PhantomJS Modern Chromium
CSS Flexbox Partial, buggy Full support
CSS Grid Not supported Full support
CSS custom properties Not supported Full support
ES6+ JavaScript (arrow functions, async/await, Promises) Not supported Full support
React / Vue / Angular rendering Fails or blank page Full support
Web fonts and emoji Unreliable Full support
WebP / AVIF images Not supported Full support
Shadow DOM Not supported Full support
Security patches None since 2016 Regular updates

There are also unpatched vulnerabilities. CVE-2019-17221 allows arbitrary file reads through crafted HTML. CVE-2020-7739 is an SSRF in the npm package. Neither will ever be fixed. If your security team runs dependency audits, PhantomJS won't pass.

Honestly, the rendering is just the start. PhantomJS binaries break on newer OS versions. The npm package downloads over HTTP, which means the binary itself is vulnerable to tampering during install. And 511 npm packages still depend on it, creating a chain of unmaintained dependencies that won't ever get patched.

Migration options: self-hosted vs managed API

Puppeteer is the most direct PhantomJS replacement. Same language (JavaScript), similar mental model (launch browser, open page, capture screenshot). Google built it specifically for headless Chrome automation, and it arrived almost exactly when PhantomJS stopped. Around 10 million weekly npm downloads in 2026. If you have PhantomJS scripts in a Node.js codebase, Puppeteer is the natural rewrite target.

Playwright came two years later from the same team that originally built Puppeteer (they moved to Microsoft). It adds multi-browser support (Chrome, Firefox, WebKit), better auto-waiting, and stronger test isolation. If you need cross-browser screenshots or are starting fresh, Playwright is worth considering. See the Puppeteer vs screenshot API and Playwright vs screenshot API comparisons for the full picture.

Both require managing Chromium on your servers. Docker images grow past a gigabyte, each browser instance consumes hundreds of megabytes of memory, and you build retry logic for crashes and timeouts. For teams that already run Node.js infrastructure and need full browser automation (click sequences, form fills, multi-step workflows), self-hosting makes sense.

Here is what often gets overlooked though: PhantomJS was language-agnostic. It was a standalone binary you could call from PHP, Ruby, Java, Python, or a shell script. Puppeteer and Playwright are Node.js libraries. If your stack is Laravel, Rails, Django, or Spring, adding Node.js as a dependency just for screenshots isn't exactly a small ask.

A managed screenshot API restores that language-agnostic simplicity. You make an HTTP request from any language and get back an image or PDF. No browser binary on your server, no Node.js runtime to maintain, no process management. ScreenshotRun runs modern Chromium in the cloud, handles scaling, and returns results through a REST endpoint. The HTML to image and URL to PDF features cover the two most common PhantomJS use cases directly.

For teams that used PhantomJS for PDF generation specifically, the wkhtmltopdf alternative guide covers that migration path in depth, since wkhtmltopdf shared the same QtWebKit engine.

Code migration and feature comparison

A typical PhantomJS script for capturing a screenshot:

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

page.viewportSize = { width: 1280, height: 800 };
page.settings.userAgent = 'Mozilla/5.0 ...';

page.open('https://example.com', function(status) {
    if (status === 'success') {
        window.setTimeout(function() {
            page.render('/tmp/screenshot.png');
            phantom.exit();
        }, 3000);
    } else {
        phantom.exit(1);
    }
});

That is around 15 lines, plus you need the PhantomJS binary installed, process management to restart it when it crashes, and timeout handling for pages that hang. I remember spending a weekend debugging why PhantomJS would silently produce empty PNGs on certain pages. Turned out the site had switched to Flexbox and PhantomJS just gave up rendering half the layout.

The same capture with a cURL call to ScreenshotRun:

curl "https://api.screenshotrun.com/v1/screenshots/capture?url=https://example.com&width=1280&height=800&delay=3000&format=png" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -o /tmp/screenshot.png

One line. Works from any language that can make HTTP requests. For Node.js:

const params = new URLSearchParams({
    url: 'https://example.com',
    width: '1280',
    height: '800',
    delay: '3000',
    format: 'png'
});

const response = await fetch(
    `https://api.screenshotrun.com/v1/screenshots/capture?${params}`,
    { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);

const buffer = Buffer.from(await response.arrayBuffer());

For Python (replacing a PhantomJS subprocess call):

import requests

response = requests.get(
    'https://api.screenshotrun.com/v1/screenshots/capture',
    params={
        'url': 'https://example.com',
        'width': '1280',
        'height': '800',
        'delay': '3000',
        'format': 'png'
    },
    headers={'Authorization': 'Bearer YOUR_API_KEY'}
)

with open('screenshot.png', 'wb') as f:
    f.write(response.content)

For PHP, Ruby, Java, and Go, the pattern is the same: HTTP GET with your parameters, save the response body. No PhantomJS binary, no Node.js runtime, no browser to manage.

Here is how the two compare across capabilities:

Capability PhantomJS ScreenshotRun API
Rendering engine QtWebKit (~2013) Chromium (latest)
JavaScript support ES5 only Full ES2024+
CSS Grid / Flexbox None / partial Full support
SPA rendering (React, Vue, Angular) Fails Full support
Output formats PNG, JPEG, PDF PNG, JPEG, WebP, AVIF, TIFF, PDF
Full page capture Manual viewport resize full_page=true
Dark mode Not supported dark_mode=true
Retina / HiDPI Manual zoomFactor workaround retina=true
Ad and cookie blocking Not built in block_ads=true, block_cookies=true
Language support Any (standalone binary) Any (REST API)
Maintenance Suspended since 2018 Active, updated Chromium
Cost Free (self-hosted) Free tier (200/month), paid plans after

When self-hosting is still the right call

A managed API is the simplest PhantomJS replacement for screenshot and PDF use cases, but it's not always the right one.

If you need full browser automation beyond screenshots, click sequences, form submissions, multi-page workflows, then Puppeteer or Playwright give you the control an API cannot. PhantomJS was a general-purpose headless browser, and if you used it that way, a general-purpose replacement makes more sense.

Air-gapped environments that cannot make outbound HTTP calls need a self-hosted solution. Same for organizations where sending URLs or HTML to an external service is a compliance issue.

If you run high volume on a tight budget, self-hosting Chromium is cheaper per screenshot once you are past a few thousand daily. You pay in engineering time instead of API fees, which may or may not be a better deal depending on your team.

For everyone else, especially teams on PHP, Ruby, Python, Java, or Go who used PhantomJS as a callable binary, the REST API approach keeps things simple. You replace one external dependency with an HTTP call, and your screenshots start rendering the modern web again. Try ScreenshotRun's free tier on a page that PhantomJS renders incorrectly and see the difference firsthand.

Frequently asked questions

No. Development was suspended in March 2018 after the maintainer stepped down, citing headless Chrome as a faster and more stable alternative. The last release (v2.1.1) was in January 2016. The GitHub repository is archived and read-only. Multiple unpatched security vulnerabilities exist, including CVE-2019-17221 (arbitrary file read) and CVE-2020-7739 (SSRF).
For self-hosted solutions, Puppeteer or Playwright with headless Chrome are the standard replacements. For a managed solution that requires no infrastructure, a screenshot API like ScreenshotRun replaces PhantomJS page.render() with a single HTTP call using modern Chromium rendering.
The concepts are similar (open page, wait, render), but the API syntax is different. PhantomJS scripts need to be rewritten. If your PhantomJS usage was limited to screenshots or PDFs, a screenshot API can reduce the migration to a single HTTP request per capture, avoiding the rewrite entirely.
Yes. It has known unpatched vulnerabilities: CVE-2019-17221 allows arbitrary file reads through crafted HTML, and CVE-2020-7739 is an SSRF vulnerability in the npm package. The npm package also downloads the binary over HTTP, making it vulnerable to tampering. None of these will ever be patched.
CasperJS was a navigation and testing framework built on top of PhantomJS. When PhantomJS was discontinued, CasperJS also became unmaintained. Developers who used CasperJS for screenshots or testing need to migrate to Puppeteer, Playwright, or a managed screenshot API.