iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Use a real browser renderer, not an HTML parser. In Laravel, Spatie Browsershot drives headless Google Chrome through Puppeteer and can render either a URL or an HTML string, then save the result as a PNG. PNG is its default image format, so a path ending in .png is usually all you need. For a Laravel-style facade, Spatie Laravel Screenshot wraps this workflow and also offers a Cloudflare Browser Rendering driver.
This guide shows complete local-rendering examples, explains timing and sizing controls, covers deployment and failure modes, and then shows a hosted option when installing Node.js and Chrome is inconvenient.
Choose the rendering route
| Route | Input | Where rendering runs | Best fit |
|---|---|---|---|
| Browsershot directly | URL or arbitrary HTML | Your server’s Puppeteer-controlled headless Chrome | Maximum low-level control when browser dependencies can be installed |
| Laravel Screenshot | Laravel facade API | Browsershot by default; Cloudflare Browser Rendering when configured | A Laravel-oriented workflow with documented defaults and queueing examples |
| Cloudflare driver for Laravel Screenshot | Laravel Screenshot requests | Cloudflare Browser Rendering | Deployments that should avoid a local Node.js installation or Chrome binary |
Official documentation does not establish a universal speed, price, or throughput winner. Compare the options by dependency management, whether you can provide HTML directly or must expose a URL, viewport and readiness controls, and whether the browser runs locally or in a hosted service.
Free tools Windows power users keep installed
One-click scans. No signup required.
Install and prepare a local Browsershot renderer
Browsershot’s path is PHP calling a Node/Puppeteer process that controls headless Chrome. Install the package versions appropriate for your Laravel application, then install the Node and browser dependencies required by the current Browsershot documentation. The exact commands and supported versions change, so use the package’s current installation guide rather than pinning an old command from a blog post.
#1 Best Overall
- Confirm that PHP can start the Node process under the same user as your queue worker or web server.
- Ensure that Chrome or the supported Chromium binary is present and executable in production.
- Give the process enough shared memory, CPU, and temporary storage for the pages you render.
- If screenshots run in a queue, install dependencies in the worker image as well as the web image.
For a Laravel facade and configurable drivers, install Spatie Laravel Screenshot and configure its default Browsershot driver as described by its current setup documentation. Its documented defaults are a 1280×800 viewport, a 2× device scale factor, PNG output, and waiting for network idle; treat those as package defaults, not immutable output guarantees.
Convert an HTML string to PNG with Browsershot
Use Browsershot::html() when the markup is already available in your application. The target filename determines the output extension in this basic example.
<?php
namespace AppHttpControllers;
use IlluminateHttpResponse;
use SpatieBrowsershotBrowsershot;
class CardController extends Controller
{
public function png(): Response
{
$html = view('cards.share', [
'title' => 'Quarterly report',
'value' => '$42,800',
])->render();
$path = storage_path('app/public/cards/report.png');
Browsershot::html($html)
->windowSize(1200, 630)
->save($path);
return response()->file($path, [
'Content-Type' => 'image/png',
]);
}
}
Create the destination directory before saving, or ensure your storage setup creates it. If you want a persistent public URL, store the file on the disk configured for your application and return a signed or authenticated download route instead of exposing a private filesystem path.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsRender a URL instead
When the page is already served and its CSS, fonts, and JavaScript resolve through normal web URLs, use Browsershot::url():
$path = storage_path('app/public/pages/home.png');
Browsershot::url('https://example.com')
->windowSize(1440, 900)
->save($path);
A URL capture follows the page’s normal network behavior. An HTML-string capture is useful for private, unsaved views, but external assets must still be reachable by the browser unless you inline them or provide accessible local URLs.
Control dimensions, content, and readiness
Viewport and full-page output
Set the viewport deliberately so responsive breakpoints produce the intended design. Use the package’s full-page option when the PNG should include content below the initial viewport. A full-page image can become extremely tall; for social cards, invoices, and thumbnails, a fixed width and height is usually safer.
Retina scale and backgrounds
A device scale factor changes pixel density without changing CSS layout dimensions. Laravel Screenshot documents a 2× default, but configure it explicitly when another consumer requires a specific pixel size. Preserve CSS backgrounds when they are part of the design, and set a transparent background only when your compositing workflow needs alpha transparency.
Windows 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 reinstallOutdated 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 matchWait for the actual page state
Network-idle waiting is convenient for pages whose requests finish, but analytics, websockets, polling, and advertising can keep a page busy indefinitely. Prefer a short, bounded delay for predictable animations or wait for a selector that proves the data is present. Browsershot’s current documentation also describes waiting for selectors or functions. Avoid an unbounded wait: always pair readiness logic with an operational timeout.
Browsershot::html($html)
->windowSize(1200, 630)
->waitForSelector('.chart-ready')
->save(storage_path('app/public/chart.png'));
Fonts, images, and JavaScript
- Use absolute, reachable URLs for remote assets, or inline critical CSS and images for an HTML-string render.
- Wait until client-side charts and lazy images have finished before capturing.
- Disable transitions or add a deterministic delay when an animation can leave an intermediate frame.
- Keep secrets out of HTML and query strings; use controlled headers or cookies only when the target page requires authentication.
Use Laravel Screenshot for a facade workflow
Laravel Screenshot provides a Laravel-focused API around screenshot generation. Its overview documents screenshot options, queueing examples, and a Browsershot driver by default. A typical controller keeps rendering concerns behind the facade while you configure dimensions and output for each job. Check the package’s current namespace and installation instructions for your installed release before copying imports, because package APIs can evolve.
The package also documents a Cloudflare Browser Rendering driver. That route sends rendering to Cloudflare and avoids requiring local Node.js or a Chrome binary, which can simplify a restricted container or serverless deployment. It is not established as universally cheaper or faster; evaluate it with your own workload, region, limits, and credentials.
Rank #3
Make captures reliable in production
Queue expensive work
Browser startup and page rendering are poor fits for a request that must return instantly. Dispatch a job, write the PNG to durable storage, and notify the caller when it is ready. Set a job timeout longer than the browser timeout, and clean up temporary files after upload.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Control concurrency
Each concurrent Chrome page consumes memory. Start with a small worker count, observe failures, and increase it only after measuring your host. Reuse a browser process only when your chosen integration safely isolates pages and clears cookies and local storage between jobs.
Make output deterministic
- Pin the browser and package versions in your deployment image.
- Use a fixed timezone, locale, and viewport when text wrapping or dates matter.
- Wait for a semantic readiness marker rather than guessing with a long delay.
- Cache identical inputs using a content hash and invalidate the cache when templates or assets change.
Troubleshooting common failures
“Chrome/Chromium not found” or process launch errors
Cause: the worker image lacks the browser, the executable path is wrong, or the runtime user cannot execute it. Fix: install the supported browser dependencies in the same image that runs the job, configure the documented executable path, and test as the queue user.
Node or Puppeteer cannot be started
Cause: Node is absent from the production image, permissions block the binary, or the PHP process has a different PATH. Fix: invoke the configured absolute paths, grant execute permission, and log the environment seen by the worker.
Blank or partially rendered PNG
Cause: capture occurred before JavaScript, fonts, lazy images, or data requests completed. Fix: wait for a selector or function tied to the finished state, verify asset URLs from the server, and disable animations.
Rank #4
Timeout while waiting for network idle
Cause: persistent polling, websockets, or third-party requests never become idle. Fix: replace network-idle waiting with a selector or bounded delay, and block nonessential requests where your integration supports it.
Different layout in production
Cause: viewport, device scale, fonts, timezone, or user-agent differs between environments. Fix: set these values explicitly and package the same fonts and browser revision in every environment.
Private assets return 401 or 403
Cause: the headless browser has no session or authorization header. Fix: provide narrowly scoped cookies or headers through the documented Browsershot options, or render assets through a controlled internal endpoint.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
ScreenshotNeo is a hosted screenshot API and MCP server. One request can render a URL as PNG, JPEG, WebP, or PDF, without you packaging Chrome. Before capture it accepts cookie/consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result.
Recommended Free Tools
Its API supports full-page and CSS-selector captures, dark mode, device presets or custom viewports, retina scale, PDF controls, custom CSS and JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
Use the same endpoint from Laravel (or any server-side PHP code):
Best Value
use IlluminateSupportFacadesHttp;
$response = Http::timeout(90)->get('https://api.screenshotneo.com/v1/shot', [
'access_key' => config('services.screenshotneo.key'),
'url' => 'https://example.com',
]);
$response->throw();
Storage::disk('public')->put('screenshots/example.webp', $response->body());
See the ScreenshotNeo documentation for authentication and options. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
Equivalent requests in cURL, Python, and Node.js
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}`);
Frequently Asked Questions
Can I convert a Blade view without publishing a route?
Yes. Render the view with view(...)->render(), pass the resulting string to Browsershot::html(), and save the PNG to storage.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Is PNG always the smallest output?
No. PNG preserves sharp text and transparency but can be larger than JPEG or WebP; choose the format required by the consuming system.
Should I use a URL or an HTML string?
Use a URL when the page’s normal asset and authentication context is already available. Use an HTML string for unsaved or private markup, while making its external assets reachable.
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.

