Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsRender the Blade view to an HTML string, then give that HTML to a browser-based renderer and save the result as a PNG. With Spatie Laravel Screenshot, the core sequence is:
use SpatieLaravelScreenshotFacadesScreenshot;
$html = view('reports.summary', $data)->render();
Screenshot::html($html)
->save(storage_path('app/reports/summary.png'));
The view supplies the markup and data; a real browser executes JavaScript, loads assets, applies CSS, and rasterizes the result. The sections below show installation, reliable asset handling, sizing, full-page output, queues, troubleshooting, and an alternative that avoids maintaining a browser runtime.
1. Install Laravel Screenshot and choose a rendering driver
Install the package with Composer:
composer require spatie/laravel-screenshot
Read the package’s current installation and setup instructions for the version of Laravel Screenshot in your application. Its default local driver uses the Browsershot stack, which requires the spatie/browsershot package, Node.js, and a Chrome or Chromium executable. The package also documents a Cloudflare Browser Rendering driver. That option does not require a local Node.js installation or Chrome binary, but it does require a Cloudflare account and service configuration.
Driver configuration and environment variables are version-sensitive. Follow the package’s driver configuration documentation rather than copying settings from an older release.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstall#1 Best Overall
Local Browsershot driver
- Rendering happens through a browser available to the application server, worker, or container.
- You control the browser binary and its network access.
- Your deployment must install and keep compatible Node.js, Browsershot dependencies, and Chrome/Chromium.
Cloudflare Browser Rendering driver
- The rendering work is performed by Cloudflare’s Browser Rendering service.
- No local Node.js or Chrome binary is needed according to the package documentation.
- You must configure a Cloudflare account and the service credentials required by the package.
The available documentation does not establish a general winner for cost, speed, privacy, or pixel fidelity. Select the driver your deployment can operate and test it with your actual views.
2. Render the Blade view, then save it as PNG
In a controller, service, or queued job, render the view exactly as you would for an HTTP response, then pass the resulting string to Screenshot::html():
<?php
namespace AppHttpControllers;
use IlluminateHttpRequest;
use SpatieLaravelScreenshotFacadesScreenshot;
class ReportImageController
{
public function store(Request $request)
{
$data = [
'title' => 'Monthly revenue',
'period' => 'September 2026',
'rows' => [
['label' => 'Subscriptions', 'value' => '$42,800'],
['label' => 'Services', 'value' => '$8,450'],
],
];
$html = view('reports.summary', $data)->render();
$path = storage_path('app/reports/summary.png');
Screenshot::html($html)->save($path);
return response()->download($path, 'summary.png');
}
}
The documented HTML workflow is described in Laravel Screenshot’s taking-screenshots guide. Combining view(...)->render() with Screenshot::html(...) is the practical Blade integration: Laravel produces the HTML, and the renderer captures it.
Create a self-contained view
For predictable output, keep the image view focused on the card, report, or document being captured:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
body { margin: 0; font-family: Arial, sans-serif; background: #f3f4f6; }
.card { width: 900px; padding: 40px; background: white; }
.row { display: flex; justify-content: space-between; padding: 12px 0; }
</style>
</head>
<body>
<main class="card">
<h1>{{ $title }}</h1>
<p>{{ $period }}</p>
@foreach ($rows as $row)
<div class="row">
<span>{{ $row['label'] }}</span>
<strong>{{ $row['value'] }}</strong>
</div>
@endforeach
</main>
</body>
</html>
Inline critical CSS when possible. A raw HTML string does not automatically have the same asset base URL as a browser visiting your application. Relative stylesheets, images, fonts, and scripts may therefore fail to load. Use absolute, reachable URLs, inline the required CSS, or capture an application URL instead.
Rank #2
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
3. Control dimensions, scale, and capture scope
Laravel Screenshot documents defaults of a 1280 × 800 viewport, device scale factor 2, PNG output, and waiting for network idle. Those defaults are convenient for a typical page, but an invoice, social card, or long report usually needs explicit settings. The package exposes Browsershot controls; the underlying capabilities are listed in Browsershot’s image-creation documentation.
Capture a fixed element
When the view contains one component and you do not want the surrounding page, select that element with the renderer’s element or clipping controls. The exact fluent method names can vary by Laravel Screenshot version, so consult the versioned API before deploying. Browsershot documents selecting an element and clipping a region.
Capture the complete page
For a long report, use the full-page option rather than relying on an 800-pixel viewport. Full-page capture expands the screenshot to the rendered document height. If the page uses lazy-loaded images, ensure those images are triggered before capture; otherwise the resulting PNG can contain empty placeholders.
Recommended Free Tools
Choose viewport and device scale deliberately
- Viewport: Set width and height to the design breakpoint you intend to represent.
- Device scale factor: A factor of 2 produces a higher-density bitmap but increases memory and file size.
- PNG: Best for sharp text, diagrams, and transparency; it is lossless and can be larger than JPEG or WebP.
- Clipping: Useful for a card or chart whose dimensions are known in advance.
Do not confuse CSS pixels with output pixels: a 900-pixel-wide element captured at a device scale factor of 2 can produce roughly 1,800 bitmap pixels across.
4. Make JavaScript and assets deterministic
JavaScript supplied in the HTML executes during capture. That enables chart libraries and client-side rendering, but it also introduces timing and network dependencies. Browsershot notes that some sites do not render correctly without JavaScript.
Rank #3
Use a stable asset base
- Prefer
https://URLs that the rendering host can reach. - Make sure private assets do not require a browser session that is absent in the renderer.
- Check CORS, firewall rules, signed URL expiry, and font responses.
- Inline small critical styles and SVGs when external loading is unnecessary.
Wait for the actual visual state
Network-idle waiting is the documented default, but network idle does not guarantee that a chart animation or asynchronous component has finished. Add a deliberate delay or wait for a selector when your package version exposes those controls. A reliable pattern is to render a hidden “ready” marker after your client-side work completes and wait for that marker.
<script>
renderChart().then(() => {
document.documentElement.dataset.captureReady = 'true';
});
</script>
Then configure the screenshot operation to wait for the corresponding selector or state using the current Laravel Screenshot/Browsershot API.
Consider URL capture instead of raw HTML
Use raw HTML when the image represents a view rendered with application data. Use URL capture when the target is an accessible page whose normal route already supplies its CSS, JavaScript, authentication, and asset base. Both HTML and URL inputs are documented by Laravel Screenshot and Browsershot.
5. Store the file safely and avoid request timeouts
Save to a path your PHP process can write, such as storage/app, and create the parent directory before capture if your deployment does not provision it:
$directory = storage_path('app/reports');
if (! is_dir($directory)) {
mkdir($directory, 0755, true);
}
Screenshot::html($html)->save($directory . '/summary.png');
If users need the file through a public disk, store it with Laravel’s filesystem abstraction and return a temporary or authorized URL rather than exposing arbitrary storage paths.
Rank #4
- Brand: Wiley
- Set of 2 Volumes
- A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
Browser startup and page rendering can exceed a normal web-request budget. Laravel Screenshot documents queued screenshot generation. Dispatch a job containing the view data or a report identifier, write the image from the worker, and notify the caller when the path is ready. Keep the job idempotent so a retry does not create conflicting filenames, and record failures with the URL or view name and renderer error.
6. Troubleshoot blank, incomplete, or incorrect PNGs
| Symptom | Likely cause | Fix |
|---|---|---|
| “Command not found” or browser launch failure | Node.js, Browsershot, or Chrome/Chromium is missing from the worker environment. | Install the versions required by the package documentation, expose the binary to the worker, and verify the queue container uses the same image as web requests. Alternatively configure the documented Cloudflare driver. |
| Blank page or missing content | JavaScript failed, a request was blocked, or capture occurred before rendering completed. | Inspect browser errors, verify network access, wait for a selector or known delay, and test the route in the same deployment environment. |
| CSS or fonts missing | Relative URLs in a raw HTML string have no usable base URL, or assets are private. | Use absolute URLs, inline critical CSS, provide valid credentials where supported, or capture the fully configured application URL. |
| Images are absent below the fold | Lazy loading has not been triggered before the full-page capture. | Scroll or otherwise trigger lazy loading in page JavaScript, wait for the images, then capture. |
| Only part of the report appears | The default 1280 × 800 viewport was captured instead of the full document. | Enable full-page capture or set an explicit clip/element region. |
| Text looks soft or file is unexpectedly large | Device scale factor and PNG’s lossless encoding increase bitmap dimensions and bytes. | Choose the required scale, remove unnecessary whitespace, and use JPEG/WebP only when their compression characteristics suit the output. |
| Works in a browser but not in a queue | The worker has different environment variables, filesystem permissions, network egress, or browser binaries. | Run a diagnostic job on the actual worker image and check writable directories, DNS, certificates, and executable paths. |
7. Test the result before shipping
- Run the capture against representative data: short text, long text, empty collections, and non-ASCII characters.
- Compare the expected viewport and full-page variants; inspect clipping at the bottom and right edges.
- Check images, web fonts, charts, and any JavaScript-generated content in the saved PNG.
- Run the same code in the production worker or container, not only on a development laptop.
- Measure capture time and memory for the largest report, then decide whether to queue it.
- Retain a failure log and a deterministic report identifier so a failed image can be regenerated.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. It accepts a URL and returns PNG, JPEG, WebP, or PDF, so your Laravel application can capture a route without installing Node.js or Chrome in the application container.
For a publicly reachable Laravel route, make one GET request (see the ScreenshotNeo documentation):
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Replace the example URL with your route and choose the output format supported by your request. The equivalent Python and Node.js calls are:
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}`);
ScreenshotNeo can accept cookie and consent banners before capture and remove more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed as clean shots, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
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. Create a free ScreenshotNeo account to try the route capture.
Best Value
8. Laravel-specific design choices
Use data, not request state, in queued jobs
Pass a report ID or a serializable data transfer object to a job. Re-query the database in the worker and render the view there. Do not serialize a live request, authenticated session, or closure.
Protect private reports
A renderer visiting a URL must be authorized without exposing credentials in a public link. Prefer raw HTML in a trusted worker for private data, or implement a short-lived signed route and revoke or expire it promptly. Never place long-lived secrets in a query string that may be logged.
Keep output naming deterministic
Include the report identifier, version, and format in the filename. Write to a temporary name and move it into place after a successful capture so consumers never read a partially written file.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Frequently Asked Questions
Can I save a Blade view directly without rendering HTML first?
No. A Blade template must first be evaluated with its data. The resulting HTML string can then be passed to the screenshot renderer, or the renderer can visit a route that returns the view.
Why does my raw HTML screenshot lose relative images and CSS?
A rendered string may not provide the browser with the same document URL and asset base as a normal route. Use absolute asset URLs, inline critical resources, or capture an accessible application URL.
Should I use a queued job for every screenshot?
Use a queue when browser startup or page rendering could exceed your HTTP timeout, or when captures are generated in batches. Small, fast captures can remain synchronous if your latency budget allows it.
Is Cloudflare rendering always better than Browsershot?
The documented options have different operational requirements. Browsershot runs through local browser dependencies; Cloudflare removes those local dependencies but requires a Cloudflare account and configuration. The available documentation does not establish a universal speed, cost, privacy, or fidelity winner.
The Bottom Line
The dependable Laravel pattern is view(...)->render() followed by Screenshot::html(...)->save(...), with explicit dimensions, asset URLs, JavaScript readiness, and a worker environment tested in production. Use Browsershot locally or the documented Cloudflare driver according to your deployment constraints; use ScreenshotNeo when an API call is simpler than maintaining a browser runtime.
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.

