Recommended Free Tools
The practical solution is to render each <div> in the browser with JavaScript and html2canvas, then serialize each canvas as a separate PNG or JPEG. PHP is not the DOM-rendering step. Add PHP only when you need to store, validate, resize, re-encode, or protect the generated files on a server.
This split keeps browser downloads simple while still supporting a controlled server workflow. It also matters for accuracy: html2canvas reconstructs an image from DOM and supported styles rather than capturing the browser compositor pixel for pixel.
Choose the right architecture
| Route | Rendering location | Best for | Trade-offs |
|---|---|---|---|
| Browser export | JavaScript and html2canvas | Users downloading one image per card or panel | No server storage; output depends on browser support, CSS, and same-origin rules |
| Browser render plus PHP | Browser creates bytes; PHP validates and processes them | Central storage, access control, image conversion, or server-generated downloads | Requires upload limits, validation, authentication, and filesystem or object-storage handling |
PHP GD can read and write supported image formats, but it cannot turn arbitrary HTML and CSS into an image by itself. A fully server-side browser renderer is a different architecture and is not required for this workflow.
Prepare the page
Give every target a stable selector. Keep each export element self-contained so its dimensions, background, and typography are known at capture time.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minute#1 Best Overall
<section class="export-card" data-name="overview">
<h2>Quarterly overview</h2>
<p>Revenue increased 18%.</p>
</section>
<section class="export-card" data-name="details">
<h2>Details</h2>
<p>Three regions exceeded target.</p>
</section>
<button id="export-all" type="button">Download images</button>
Include the html2canvas library used by your project, then wait for fonts and images before exporting. The official examples demonstrate selecting an element, calling html2canvas, serializing with toDataURL('image/png'), and downloading through a link: html2canvas examples.
Export every div as its own PNG
This async loop renders one element, waits for completion, assigns a unique filename, and starts the next render only after the previous one has finished.
async function waitForImages(root) {
const images = [...root.querySelectorAll('img')];
await Promise.all(images.map(img => {
if (img.complete) return Promise.resolve();
return new Promise(resolve => {
img.addEventListener('load', resolve, { once: true });
img.addEventListener('error', resolve, { once: true });
});
}));
}
function safeName(value, fallback) {
const cleaned = (value || '').replace(/[^a-z0-9_-]+/gi, '-').replace(/^-|-$/g, '');
return cleaned || fallback;
}
async function exportDivsAsPng() {
const elements = [...document.querySelectorAll('.export-card')];
if (!elements.length) throw new Error('No .export-card elements found');
await document.fonts.ready;
for (const [index, element] of elements.entries()) {
await waitForImages(element);
const canvas = await html2canvas(element, {
scale: window.devicePixelRatio,
backgroundColor: null,
useCORS: true
});
const link = document.createElement('a');
const label = safeName(element.dataset.name, `card-${index + 1}`);
link.download = `${label}.png`;
link.href = canvas.toDataURL('image/png');
link.click();
}
}
document.querySelector('#export-all').addEventListener('click', () => {
exportDivsAsPng().catch(error => {
console.error(error);
alert('The images could not be exported. Check the console for details.');
});
});
devicePixelRatio produces sharper output on retina displays, but it also increases canvas memory use. Set a fixed scale such as 1 when predictable dimensions matter more than density.
Export JPEG instead of PNG
JPEG is useful for photographic cards and usually produces smaller files, but it cannot preserve transparency. Pass a quality value between 0 and 1; the exact output and format support should be checked in the browsers you support. Canvas serialization behavior is described in Apple’s WebKit documentation: HTMLCanvasElement.toDataURL.
Rank #2
const canvas = await html2canvas(element, {
scale: window.devicePixelRatio,
backgroundColor: '#ffffff'
});
const quality = 0. nueve; // replace with a number such as 0.9
const jpegData = canvas.toDataURL('image/jpeg', quality);
const link = document.createElement('a');
link.download = 'card.jpg';
link.href = jpegData;
link.click();
Replace the illustrative quality assignment with valid JavaScript: const quality = 0.9;. A solid background is recommended because transparent pixels become browser- or encoder-dependent when converted to JPEG.
Use toBlob() for many or large images
toDataURL() creates a large base64 string in memory. For batches, encode to a Blob, create an object URL, download it, and revoke that URL immediately after the click.
function canvasBlob(canvas, type, quality) {
return new Promise((resolve, reject) => {
canvas.toBlob(blob => blob ? resolve(blob) : reject(new Error('Encoding failed')), type, quality);
});
}
async function downloadCanvas(canvas, filename, type = 'image/png', quality) {
const blob = await canvasBlob(canvas, type, quality);
const objectUrl = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = objectUrl;
link.download = filename;
link.click();
setTimeout(() => URL.revokeObjectURL(objectUrl), 0);
}
Browsers may block a large burst of automatic downloads. If that happens, package files into a server-side archive or ask the user to click once per file rather than bypassing browser download protections.
Send the generated image to PHP
When the application needs persistence or centralized processing, upload the blob with fetch. Do not rely on the client filename or MIME type as validation.
async function uploadCanvas(canvas, name) {
const blob = await canvasBlob(canvas, 'image/png');
const form = new FormData();
form.append('image', blob, `${name}.png`);
const response = await fetch('/save-image.php', {
method: 'POST',
body: form,
credentials: 'include'
});
if (!response.ok) throw new Error(`Upload failed: ${response.status}`);
return response.json();
}
A minimal PHP endpoint can inspect the decoded bytes with GD, create a server-generated filename, and save the image outside a publicly writable directory.
<?php
declare(strict_types=1);
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_FILES['image'])) {
http_response_code(400);
echo json_encode(['error' => 'Image upload is required']);
exit;
}
$file = $_FILES['image'];
if ($file['error'] !== UPLOAD_ERR_OK || $file['size'] > 10 * 1024 * 1024) {
http_response_code(400);
echo json_encode(['error' => 'Upload failed or exceeds the size limit']);
exit;
}
$bytes = file_get_contents($file['tmp_name']);
$image = @imagecreatefromstring($bytes);
if ($image === false) {
http_response_code(415);
echo json_encode(['error' => 'Unsupported or corrupt image data']);
exit;
}
$directory = __DIR__ . '/private-images';
if (!is_dir($directory)) mkdir($directory, 0700, true);
$filename = bin2hex(random_bytes(16)) . '.png';
$path = $directory . '/' . $filename;
if (!imagepng($image, $path, 6)) {
imagedestroy($image);
http_response_code(500);
echo json_encode(['error' => 'Could not save image']);
exit;
}
imagedestroy($image);
echo json_encode(['file' => $filename]);
PHP’s GD manual documents supported image functions. imagecreatefromstring() returns false for unrecognized, corrupt, or unsupported data and may issue a warning, so production code should log safely and avoid exposing filesystem details.
Security, fidelity, and browser constraints
- Cross-origin images: images must be same-origin or served with suitable CORS headers and an appropriate html2canvas configuration. Otherwise the canvas can become tainted and cannot be read back.
- Iframes: browser security rules prevent reading cross-origin iframe contents. Capture the iframe’s own page or redesign the content for export.
- CSS differences: html2canvas supports a subset of CSS and may omit or approximate unsupported effects. The project describes the result as based on DOM information, not a guaranteed exact representation: html2canvas documentation.
- Hidden content: an element with
display:nonehas no layout to capture. Move it off-screen or render it in a visible export container before capture. - Fonts and lazy assets: wait for
document.fonts.readyand image completion; otherwise text can reflow or appear with fallback fonts. - Canvas limits: very large dimensions can exceed browser memory or maximum canvas size. Reduce scale, split a long page, or export smaller elements.
- Input validation: enforce request-size and authentication limits, decode the actual bytes, generate your own filename, and store files where users cannot execute uploaded content.
PNG or JPEG: a practical decision
| Content | Format | Reason |
|---|---|---|
| UI cards, text, charts, line art | PNG | Sharp edges and lossless output; supports transparency |
| Photos or photographic backgrounds | JPEG | Usually smaller; choose quality deliberately |
| Any design requiring transparent pixels | PNG | JPEG has no transparency channel |
Troubleshooting checklist
“html2canvas is not defined”
The library was not loaded before your export script, or the script ran in the wrong order. Load html2canvas first and confirm the global name in the browser console.
Images are missing or the export throws a security error
Inspect image origins and response headers. Move assets to the same origin or configure CORS on the image host; setting useCORS cannot override a server that sends no permission.
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #4
The result differs from the page
Check unsupported CSS, pseudo-elements, filters, blend modes, web fonts, and animations. Freeze animations, wait for fonts, and test the exact browser versions your users run.
PHP rejects a valid-looking upload
Check PHP upload limits, temporary-directory permissions, GD format support, and the actual decoded bytes. Do not accept a client-supplied extension as proof of type.
Downloads stop after the first few files
This is commonly browser anti-abuse behavior. Use a user gesture for each download, create a ZIP on the server, or upload blobs and return one archive.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
For a remote URL rather than DOM elements already open in your page, ScreenshotNeo provides a one-call screenshot API. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status. It also offers an MCP server for AI agents through take_screenshot, get_page_info, and capture_pdf.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →See the ScreenshotNeo API documentation for all options, including element selectors, full-page lazy-image loading, device and retina settings, custom CSS and JavaScript, waits, request blocking, headers, cookies, geolocation, PDF output, caching, signed links, webhooks, bulk capture, and usage reporting.
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}`);
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots, and every feature is included on every plan. Create a free ScreenshotNeo account.
Frequently Asked Questions
Can PHP convert a live div directly into a PNG?
No. Render the DOM in a browser first; PHP can then validate, store, or re-encode the resulting bytes with GD.
Why is my export not pixel-perfect?
html2canvas rebuilds the image from DOM and supported styles, so unsupported CSS, fonts, cross-origin assets, and browser differences can change the result.
Should I use one canvas for all divs or one per div?
Use one canvas per target when you need separate files and filenames. A single combined canvas is appropriate only when one composite image is the desired output.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




