How to Convert an HTML Page to an SVG Image with PhantomJS
PhantomJS cannot export arbitrary HTML to SVG. Learn the supported capture workflow, true-vector alternatives, code, limits, and a modern API option.
Short answer: PhantomJS does not document SVG as an output format for page.render(). Its documented formats are PDF, PNG, JPEG, BMP, PPM and, depending on the Qt build, GIF. PhantomJS can display SVG that is already part of a page, but that is different from exporting an arbitrary HTML layout as a genuine SVG file. See the render API and screen-capture guide.
If a raster image is acceptable, render a PNG (or another supported format). If you need editable vector paths and text, use a separate workflow that constructs or translates vector content; placing a PNG inside an SVG wrapper does not make the page vector artwork.
1. What PhantomJS can and cannot convert
| Requirement | PhantomJS answer |
|---|---|
| Screenshot of an HTML page | Supported with page.render(), normally as PNG, JPEG or PDF. |
| Render inline or external SVG already in the page | Supported as browser content when the page loads correctly. |
| Export arbitrary HTML/CSS layout as SVG paths | Not provided by the documented API. |
Wrap a screenshot in an <svg> element |
Possible, but the embedded image remains raster. |
The distinction matters: HTML layout contains text, CSS boxes, images, shadows and browser-specific effects. A screenshot records their pixels. A true SVG conversion must recreate suitable vector primitives, text and image references.
2. Render the page to PNG with PhantomJS
Install PhantomJS using the package appropriate for your operating system, then save this script as capture.js. The official quick start uses page.open(), checks for a success status and calls page.render().
var page = require('webpage').create();
page.viewportSize = { width: 1440, height: 900 };
page.open('https://example.com/', function (status) {
if (status === 'success') {
page.render('page.png');
console.log('Saved page.png');
} else {
console.log('Page failed to load: ' + status);
}
phantom.exit();
});
Run it with:
phantomjs capture.js
viewportSize controls the browser viewport. This example captures the visible viewport; it does not automatically create a full-page image. Use clipRect when you need a specific region:
page.clipRect = { top: 0, left: 0, width: 1200, height: 1600 };
page.render('region.png');
Wait for asynchronous content
The load callback only tells you that the initial navigation completed. JavaScript frameworks, delayed images, web fonts and API calls may still be updating the page. Set a completion flag from page code, or use a bounded delay:
var page = require('webpage').create();
page.viewportSize = { width: 1440, height: 900 };
page.open('https://example.com/', function (status) {
if (status !== 'success') {
console.log('Load failed: ' + status);
phantom.exit(1);
return;
}
window.setTimeout(function () {
page.render('after-delay.png');
phantom.exit();
}, 2000);
});
For production captures, prefer a site-specific readiness signal over an arbitrary long delay, and always keep a timeout so a broken page cannot leave the process running forever.
3. Render HTML supplied as a string
When the HTML is already in memory, setContent(html, baseUrl) loads it without making a normal navigation request. The base URL resolves relative stylesheets, images and fonts.
var page = require('webpage').create();
page.viewportSize = { width: 1200, height: 800 };
var html = '<!doctype html>' +
'<html><head><style>body{font-family:sans-serif}h1{color:#245}</style></head>' +
'<body><h1>Invoice</h1><p>Rendered from a string.</p></body></html>';
page.setContent(html, 'https://example.com/');
window.setTimeout(function () {
page.render('string-page.png');
phantom.exit();
}, 500);
4. If you truly need SVG output
- Build vectors from structured data. Generate an SVG document directly with elements such as
<rect>,<text>,<path>and referenced images. - Use an HTML-to-vector translator. Choose a maintained tool that explicitly supports the CSS and SVG features your page uses, then verify fonts, filters, gradients, transforms and external assets.
- Separate the page and the artwork. If the page contains charts or diagrams, export those components as SVG while capturing the surrounding page as PNG or PDF.
- Accept a raster image inside an SVG container only when compatibility requires an SVG file. Embed the PNG with
<image href="data:image/png;base64,...">, and document that the pixels are not editable vectors.
Test the result in the consumers that matter. SVG support for filters, fonts and external resources varies, and a browser screenshot may include effects that have no direct SVG equivalent.
5. PhantomJS options that affect capture
| Option | Use | Typical mistake |
|---|---|---|
viewportSize |
Set viewport width and height before loading. | Expecting it to make a full-page image. |
clipRect |
Limit output to a top, left, width and height rectangle. | Clipping content because coordinates are outside the viewport. |
page.settings.userAgent |
Send a site-compatible user agent before navigation. | Changing it after page.open(). |
page.customHeaders |
Provide required request headers. | Assuming browser cookies or authentication exist automatically. |
page.onResourceError |
Log failed asset requests. | Ignoring missing fonts, CSS or images. |
6. Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
page.render('page.svg') fails or produces an unexpected file |
SVG is not a documented render format. | Render PNG/JPEG/PDF, or use a separate vector-generation workflow. |
| Blank or partially rendered image | Navigation failed, scripts are still running, or required assets were blocked. | Check the status, log resource errors, wait for readiness, and verify the URL from the capture host. |
| Fonts differ | The font is unavailable, loaded late, or unsupported by the legacy engine. | Install or embed the needed font where possible, wait for font loading, and compare against a current browser. |
| Images are missing | Relative URLs lack a correct base URL, or cross-origin/authentication rules prevent loading. | Pass the correct baseUrl to setContent, use absolute asset URLs, and provide required headers or cookies. |
| Modern site layout is broken | PhantomJS uses an old QtWebKit engine and lacks many current web APIs. | Use a maintained browser engine for new captures, or simplify the page for this legacy runtime. |
| Process never exits | A request, timer or page script remains active. | Set an application timeout and call phantom.exit() on every success and failure path. |
| Only the top of a long page appears | The viewport screenshot is being captured. | Measure the document and set a suitable clipRect, or capture sections separately. |
7. Reliability, performance and maintenance
- Reliability: Treat the callback status as a required check. Log the URL, status and failed resources so you can distinguish a page failure from an output-format limitation.
- Performance: Large viewports, long pages, web fonts and many images increase rendering time and memory. Capture only the region you need and avoid unbounded waits.
- Reproducibility: Pin the PhantomJS binary and page inputs. Legacy QtWebKit behavior can differ from current Chrome or Firefox, especially for CSS and JavaScript.
- Maintenance: The project homepage states, “Important: PhantomJS development is suspended until further notice.” Treat it as legacy software and plan a migration for new systems.
- Cost: PhantomJS itself does not provide a hosted capture service. Your costs are the machine, storage, bandwidth and engineering time required to operate and maintain the workflow.
8. Or skip the browser setup
ScreenshotNeo provides a hosted screenshot API when you need an image or PDF without maintaining PhantomJS. It returns PNG, JPEG, WebP or PDF from one GET request. The API documentation is at screenshotneo.com/docs/.
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)
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}`);
Before capture, ScreenshotNeo accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response reports the result in X-Page-Verdict and X-Billed headers. It also offers an MCP server for AI agents with 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 shots. Create a free ScreenshotNeo account.
9. FAQ
Can PhantomJS convert an HTML page directly to SVG?
No. Its documented renderer formats do not include SVG, so a direct page.render() export is not supported.
Does an SVG element on the page change the answer?
No. PhantomJS can display existing SVG content, but the complete HTML page is still rendered as a raster or PDF output.
Is PNG inside SVG considered vector?
No. It is a raster image packaged in an SVG document and remains pixel-based.
Should new projects use PhantomJS?
Usually not. The project is suspended and its QtWebKit engine is legacy. Use it only when its limitations are acceptable or when maintaining an existing system.
What is the simplest hosted replacement for a screenshot?
Use ScreenshotNeo’s one-call API when PNG, JPEG, WebP or PDF output is sufficient and you do not want to manage a browser runtime.
Sources: PhantomJS render API, screen-capture guide, project homepage, quick start, and page.open API.


