ScreenshotNeo

BlogHow-to

Create Product Listing Preview Images for a Meesho Catalog with Laravel Browsershot

Build a Laravel product preview from real catalog data, render it with Browsershot, and save a PNG or JPEG for review before upload.

By the ScreenshotNeo team4 October 20268 min read

Use Laravel to render a preview template with the product’s real image and catalog data, then use Spatie Browsershot to capture that page as a PNG or JPEG. Browsershot controls Puppeteer and headless Chrome; it accepts either a URL or HTML input. Treat the result as a review preview, not as a replacement for an authentic product photograph.

For shirts, Meesho’s reviewed seller guidance recommends a clear front product image, JPEG, no logo or watermark, and avoiding partial-product or prop images. That guidance is specific to shirts; check the current requirements for the actual category before uploading. Meesho seller guidance

1. Install Browsershot and its browser dependencies

Browsershot requires PHP, Node.js, Puppeteer, and a compatible Chrome or Chromium installation. Follow the current Browsershot requirements for your operating system and deployment environment. Install the PHP package with Composer:

composer require spatie/browsershot

Install Puppeteer in the project environment as described by the Browsershot documentation, and ensure the runtime user can launch Chrome. On a production server, confirm required shared libraries and writable temporary/cache directories are available. If Chrome or Node lives outside standard paths, Browsershot provides configuration methods such as setNodeBinary(), setNpmBinary(), setChromePath(), and setNodeModulePath(); consult the versioned configuration documentation for exact deployment needs.

2. Create a preview template using real product data

Keep the preview layout separate from your normal store page. Include only fields you want to inspect, for example title, price, variant, and a genuine product image. Avoid decorative marks that could be mistaken for part of the product or violate the applicable marketplace image instructions.

A Blade view such as resources/views/catalog/preview.blade.php can provide a predictable canvas:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    * { box-sizing: border-box; }
    body { margin: 0; font-family: Arial, sans-serif; color: #222; background: #fff; }
    .preview { width: 900px; padding: 32px; }
    .photo { width: 100%; height: 620px; object-fit: contain; background: #f7f7f7; }
    h1 { margin: 20px 0 8px; font-size: 30px; }
    p { margin: 6px 0; font-size: 20px; }
  </style>
</head>
<body>
  <main class="preview">
    <img class="photo" src="{{ $imageUrl }}" alt="{{ $product->name }}">
    <h1>{{ $product->name }}</h1>
    <p>Price: {{ $price }}</p>
    <p>Variant: {{ $variant }}</p>
  </main>
</body>
</html>

Use escaped Blade output for text fields. Ensure the image URL is reachable by Chrome from the machine performing the capture. For private storage, generate a temporary URL with a lifetime long enough for the capture, or use an authenticated page/request setup. A local filesystem path is not automatically a browser-accessible URL.

3. Capture the preview with Browsershot

For a Laravel route that renders the Blade view, capture the element with a stable CSS selector. This example returns a PNG response and waits for network activity to settle before capturing:

<?php

namespace App\Http\Controllers;

use App\Models\Product;
use Illuminate\Http\Request;
use Spatie\Browsershot\Browsershot;

class ProductPreviewController
{
    public function show(Request $request, Product $product)
    {
        $html = view('catalog.preview', [
            'product' => $product,
            'imageUrl' => $product->image_url,
            'price' => $product->price,
            'variant' => $product->variant,
        ])->render();

        $png = Browsershot::html($html)
            ->windowSize(900, 780)
            ->select('.preview')
            ->waitUntilNetworkIdle()
            ->setScreenshotType('png')
            ->screenshot();

        return response($png, 200, [
            'Content-Type' => 'image/png',
            'Content-Disposition' => 'inline; filename="product-preview.png"',
        ]);
    }
}

Adapt the model fields and route binding to your application. If external fonts, analytics, or unrelated resources keep network activity open, remove them from the preview template or use a suitable explicit wait strategy. For a simple static template, waiting for network idle may be unnecessary.

Save a file instead of returning a response

$path = storage_path('app/previews/product-' . $product->id . '.png');

Browsershot::html($html)
    ->windowSize(900, 780)
    ->select('.preview')
    ->waitUntilNetworkIdle()
    ->save($path);

Store generated previews in a controlled location, and avoid using an untrusted user-supplied path as the output filename.

4. Choose capture size, scope, and image format

Browsershot’s image options let you match the capture to the preview’s purpose. The documented image guide covers dimensions, full-page capture, selected elements, device scale, and output format. Browsershot image documentation

Choice Use it when Things to check
Viewport screenshot The complete preview fits within a fixed canvas. Set a deliberate browser window size; content outside it may be clipped.
Selected element You want just the product card or image region. Use a selector unique to the preview, such as .preview.
Full page The preview is taller than the viewport and all page content is wanted. Long pages create larger files and may include unrelated page sections.
PNG You need lossless output or sharp text and flat colors. Files can be larger than JPEG for photographic content.
JPEG You need a compressed photographic preview. Choose an appropriate quality; lossy compression can soften edges and text.
Device scale factor You want more pixels per CSS pixel for a sharper preview. Higher scale increases memory use, rendering time, and file size.

Example JPEG capture with a quality setting:

Browsershot::html($html)
    ->windowSize(900, 780)
    ->select('.preview')
    ->waitUntilNetworkIdle()
    ->setScreenshotType('jpeg', 85)
    ->save(storage_path('app/previews/product-preview.jpg'));

Check the installed Browsershot version’s method signature if a method differs from the examples; the cited documentation is for version 4. Do not assume that a particular image width, file-size limit, or approval result applies to every Meesho category.

5. Make image and font loading reliable

  • Use stable image URLs. Confirm Chrome can retrieve the image without a login session or expired signature.
  • Wait for required resources. Browsershot documents network-idle waiting for pages that load images or webfonts asynchronously. Keep the preview page lean so “idle” can occur.
  • Use an explicit selector when appropriate. Waiting for the preview element to appear helps when the page renders it after JavaScript runs; it does not guarantee every image inside has finished decoding.
  • Prefer deterministic markup. Avoid animation, rotating carousels, and time-dependent content. If needed, add capture-only CSS to disable animation and transitions.
  • Check the output dimensions and content. Open the generated image and verify the full product, crop, text, and background before using it in a workflow.

For lazy-loaded images, make sure the image is actually brought into the rendered page and loaded before capture. Browsershot’s image guide describes waiting for network idle for lazy resources and webfonts, but page-specific behavior can still require an explicit wait or a simpler template.

6. Troubleshoot common failures

Symptom Likely cause Fix
Node, npm, Puppeteer, or Chrome executable not found The web server runs with a different PATH or runtime user than the shell. Configure the Node, npm, Chrome, or node module paths in Browsershot and verify permissions as the service user.
Chrome exits or reports missing shared libraries Production host lacks a browser dependency or sandbox setup required by the installed Chrome build. Install dependencies following the official Browsershot requirements and use the documented environment-specific launch configuration.
Image is blank or missing Chrome cannot access the URL, a signed URL expired, or capture happened before loading. Test the URL from the capture host, extend the temporary URL lifetime, and wait for the image or network idle.
Fonts differ from the expected design Remote fonts failed, loaded too late, or are unavailable to the server. Use a locally available or reachable font and wait for resources before the capture.
Preview is clipped The viewport is too small or the selected element does not cover the intended content. Increase window dimensions, capture the correct selector, or use full-page mode when the full document is needed.
Capture hangs while waiting for idle Long polling, analytics, or persistent connections prevent the page from becoming idle. Remove unnecessary scripts from the preview route or choose a bounded wait for the specific content required.
Text appears unescaped or breaks the markup Product data was inserted into HTML without escaping. Use Blade’s escaped output for text and validate trusted HTML separately.
Slow requests or memory pressure Concurrent Chrome processes, oversized images, or high device scale consume resources. Queue captures, limit concurrency, resize source assets where suitable, and use only the scale and capture area needed.

7. Performance, reliability, and cost

Each Browsershot capture launches or drives a headless browser, so it uses more CPU and memory than simply returning an existing file. For occasional previews, a synchronous response may be sufficient. For bulk catalog previews or slow pages, dispatch a queued job, cap worker concurrency, set a request/job timeout that accounts for browser startup and resource loading, and retain a clear failure status for retry.

Reduce work by rendering a small dedicated template, excluding analytics and unrelated scripts, using appropriately sized source images, and capturing only the required element. Cache the generated preview when the product data, image, and layout have not changed. Account for temporary storage, browser dependencies, server CPU/RAM, and queue time in operating cost; the dossier provides no benchmark or fixed per-image runtime, so measure against your own deployment.

For reliability, record the product identifier, capture timestamp, output format, and failure reason. Retry transient network or browser failures with a limit rather than retrying indefinitely. Review the final image and confirm the current category rules before upload; a successful screenshot only means an image was rendered.

8. Check Meesho’s category guidance before use

The reviewed Meesho guidance concerns shirts: clear front product image, JPEG, no logo or watermark, and no partial shirt or props. It does not establish universal rules for every product type, exact dimensions, file-size ceilings, or guaranteed catalog approval. Confirm the current requirements for the product category and preserve an authentic representation of the product.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. Send one GET request with a URL to get a PNG, JPEG, WebP, or PDF; see the API documentation. The example below captures a publicly accessible preview URL as WebP:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://your-site.example/product-preview -o shot.webp

Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents take screenshots. 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000.

Sign up free for 1,000 screenshots a month, no card required.

FAQ

Can I capture HTML without creating a public Laravel route?

Yes. Browsershot accepts HTML input, as in the example. Make sure referenced images, stylesheets, and fonts are still reachable by the browser process; inline or locally serve assets when that is appropriate.

Does a successful Browsershot capture mean Meesho will accept the image?

No. It confirms that Chrome rendered an image. Check the current rules for the product category and review the result before upload.

Should I use PNG or JPEG for a shirt preview?

Browsershot supports both. The reviewed Meesho shirt guidance says JPEG for shirt uploads, but choose the actual upload format based on current category instructions. PNG can be useful for internal review when lossless output matters.

Can I use a generated preview as the product’s authentic listing photo?

Use the real product image and ensure the composition accurately represents the item. A designed preview is useful for review, but do not imply it is a substitute for the authentic product photograph or category-compliant source image.