Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Render 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<!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
Sale
HTML and CSS: Design and Build Websites
  • 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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

  1. Run the capture against representative data: short text, long text, empty collections, and non-ASCII characters.
  2. Compare the expected viewport and full-page variants; inspect clipping at the bottom and right edges.
  3. Check images, web fonts, charts, and any JavaScript-generated content in the saved PNG.
  4. Run the same code in the production worker or container, not only on a development laptop.
  5. Measure capture time and memory for the largest report, then decide whether to queue it.
  6. Retain a failure log and a deterministic report identifier so a failed image can be regenerated.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.