Take Full-Page Screenshots of Indian Product Detail Pages in Laravel Browsershot
Capture long Indian product pages with Laravel Browsershot. Configure full-page output, viewport, lazy-loaded content, and production troubleshooting.
Use Spatie Browsershot’s fullPage() before saving the image. It asks the headless browser to capture the full rendered page rather than only the visible viewport. The key is to choose the viewport and wait strategy for the actual product page: below-the-fold product images, reviews, and variant sections may load only after scrolling or waiting.
<?php
use Spatie\\Browsershot\\Browsershot;
$productUrl = 'https://example.in/products/example-item';
$outputPath = storage_path('app/screenshots/product.png');
Browsershot::url($productUrl)
->fullPage()
->save($outputPath);
Browsershot renders with Puppeteer running headless Chrome. See the Browsershot repository and its image creation documentation. Check the result at the target URL and viewport before relying on it in a batch job.
1. Install and prepare the Laravel application
Install Browsershot using the instructions for the version in your application, then ensure its browser runtime is available. The project README describes Puppeteer and headless Chrome. Node.js and Chrome or Chromium are requirements documented for the separate Laravel Screenshot wrapper’s Browsershot driver; verify the dependencies for the precise package and version you install rather than applying wrapper requirements blindly.
Create a private output directory and ensure the Laravel process can write to it:
mkdir -p storage/app/screenshots
Do not expose arbitrary user-supplied URLs directly to a capture worker. Validate allowed URL schemes and destinations, and consider restricting hosts to the merchants your application is intended to capture. Browsershot’s PDF documentation explicitly places URL and HTML validation responsibility on the caller; the same discipline is appropriate for screenshot jobs.
2. Capture the full rendered page
For a public product detail page, the minimum implementation is:
use Spatie\\Browsershot\\Browsershot;
Browsershot::url($productUrl)
->fullPage()
->save(storage_path('app/screenshots/product.png'));
fullPage() controls page length. It does not decide whether the page should look like desktop or mobile, nor does it guarantee that content which loads later has appeared. Those are separate choices.
Choose the viewport deliberately
Match the viewport to the page presentation you want to preserve. A desktop-width capture can show a different layout, product gallery, or navigation from a mobile-width capture. Browsershot’s image documentation covers viewport sizing and mobile emulation; consult it for the exact methods supported by your installed version. Do not assume that a desktop full-page capture represents the mobile product experience.
When capturing both layouts, make two captures with the corresponding viewport configuration and label the files accordingly. Record the viewport with the capture metadata so later comparisons use the same dimensions.
3. Make sure lazy-loaded product content is present
Many long product pages defer images or sections until they approach the viewport. A full-page screenshot requests a tall capture, but a page’s own lazy-loading behavior may still mean some resources have not loaded. Inspect the output for missing gallery images, variant controls, reviews, and recommendations.
Browsershot documents waiting for lazy-loaded resources. Use the documented approach appropriate to your version, or add a site-appropriate readiness step that scrolls through the page and waits for the content you need. Avoid assuming a single fixed delay works for every merchant: network conditions and page scripts vary.
- Capture one representative product page at the intended viewport.
- Check the top, middle, and bottom for unloaded images or absent sections.
- If content appears only after scrolling, use the documented lazy-resource wait behavior or a controlled scroll-and-wait readiness step.
- Repeat the capture and verify the output. Keep the readiness condition as specific as possible, such as waiting for a known product section to render.
For dynamic pages, “network idle” alone may not mean the product page is complete: analytics or chat connections can remain active, while a lazy-loaded section may not start loading until scroll. Prefer a readiness condition tied to the content your workflow needs, with an upper timeout.
4. Choose an output format
Browsershot’s image documentation describes PNG output by default and supports image controls including JPEG with a quality setting. PNG is useful when you need lossless output or plan to inspect fine details. JPEG can reduce file size for photographic product imagery when some compression is acceptable. Compare the resulting file and visual quality for your use case; there is no universal best format.
Save to a path writable by the application and ensure the destination directory exists. If the image will be served to users, store it using the application’s normal private/public storage rules rather than assuming a local storage path is web-accessible.
5. Laravel job example for repeatable captures
For captures triggered by a web request, move browser work to a queue so a slow merchant page does not hold the user request open. This example shows the capture logic inside a job; add the usual queue configuration and dispatch it from your application.
<?php
namespace App\\Jobs;
use Spatie\\Browsershot\\Browsershot;
use Illuminate\\Bus\\Queueable;
use Illuminate\\Contracts\\Queue\\ShouldQueue;
use Illuminate\\Foundation\\Bus\\Dispatchable;
use Illuminate\\Queue\\InteractsWithQueue;
use Illuminate\\Queue\\SerializesModels;
class CaptureProductPage implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(
public string $productUrl,
public string $relativeFileName,
) {}
public function handle(): void
{
$allowedHost = 'example.in';
$parts = parse_url($this->productUrl);
if (($parts['scheme'] ?? null) !== 'https'
|| ($parts['host'] ?? null) !== $allowedHost) {
throw new \\InvalidArgumentException('Product URL is not allowed.');
}
$directory = storage_path('app/screenshots');
if (! is_dir($directory) && ! mkdir($directory, 0750, true) && ! is_dir($directory)) {
throw new \\RuntimeException('Could not create screenshot directory.');
}
$fileName = basename($this->relativeFileName);
$path = $directory . DIRECTORY_SEPARATOR . $fileName;
Browsershot::url($this->productUrl)
->fullPage()
->save($path);
}
}
Adapt the allowed host check to your application’s rules. For multiple merchants, use an explicit allowlist and account for subdomains intentionally. A basic hostname check does not by itself prevent every network-routing risk, so production systems that accept URLs should also enforce outbound network restrictions.
6. India-specific page considerations
The capture mechanism is not specific to India, and the available documentation establishes no universal behavior for Indian merchants. Test the exact target URL, desired viewport, and runtime. Merchant pages may differ in localization, availability, pricing, region-dependent content, consent prompts, and access controls; do not assume these details from the country in the URL alone.
- Use the exact regional product URL and confirm the page shows the intended language, currency, and availability.
- Decide whether the capture should reflect a signed-in session, selected variant, or chosen delivery location. A screenshot only reflects the browser session and page state configured for that capture.
- Check that the product page is accessible from the machine or browser service performing the capture.
- Inspect consent dialogs and other overlays. They may obscure page content unless the page state or capture workflow handles them.
7. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| Only the visible screen is captured | fullPage() is missing or the installed version/configuration differs. |
Apply fullPage() before saving and check the image creation documentation for your Browsershot version. |
| Images or sections are missing near the bottom | Lazy loading has not been triggered, or the capture happened before the resource loaded. | Use Browsershot’s documented lazy-resource waiting facilities or a site-appropriate scroll/readiness step; inspect the output again. |
| The image uses the wrong layout | The viewport or mobile emulation does not match the intended presentation. | Set the viewport intentionally and capture desktop and mobile separately when both matter. |
| Capture fails because the browser cannot start | The runtime environment may lack the browser dependencies or have incompatible package configuration. | Check the installed Browsershot version’s setup instructions and browser availability. Do not infer the underlying package requirements solely from the separate Laravel Screenshot wrapper. |
| Works locally but fails in production | The production worker may have different runtime dependencies, permissions, network access, or writable paths. | Check the queue worker environment, browser executable availability, outbound access to the merchant, and write permissions on the destination directory. |
| The page is blank or shows an access challenge | The target page may be unavailable to the capture environment or may require a state the browser does not have. | Open the same URL from the capture environment, review the intended session and access requirements, and handle failure explicitly rather than treating a blank image as success. |
| Very tall capture consumes too much memory or storage | A long page with large images creates a large raster output. | Capture only the pages and viewport sizes required, select an appropriate image format, and manage output retention. If one extremely tall image is not essential, consider whether a PDF or segmented workflow better fits the use case. |
8. Performance, reliability, and cost
A full-page capture requires rendering the page and producing an image whose height follows the rendered content. Long pages and large image assets can increase processing time, memory use, and output size. There are no universal performance figures in the cited package documentation, so measure with representative target pages in the deployment environment.
- Bound the work: Set appropriate job timeouts and retries in your queue configuration, and avoid retrying permanent errors such as an invalid URL indefinitely.
- Validate results: Treat the output as successful only if the browser produced a usable image. Where the workflow depends on particular page sections, inspect or validate those sections too.
- Control concurrency: Browser processes can be resource-intensive. Tune queue concurrency to the memory and CPU available to workers.
- Manage storage: Use a retention policy for generated screenshots, especially for repeated captures of changing product pages.
- Check access and policy: Confirm that your intended capture use and page access comply with the target site’s requirements.
Browsershot is a software package; this approach does not introduce a per-screenshot ScreenshotNeo charge. Your operational costs depend on the infrastructure and storage you choose. For an alternative runtime, Spatie’s separate Laravel Screenshot documentation describes a Cloudflare Browser Rendering driver requiring Cloudflare Browser Rendering, a suitably permissioned API token, and account ID. That is a deployment alternative to evaluate when local browser binaries are unsuitable, not a requirement for ordinary Browsershot use.
9. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. A GET request returns a PNG, JPEG, WebP, or PDF, so you can request a screenshot without installing and operating a local browser for this capture. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.in/products/example-item -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={
"access_key": "YOUR_API_KEY",
"url": "https://example.in/products/example-item",
},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.in/products/example-item'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));
Cookie banners are accepted and removed along with 60+ known consent platforms, newsletter popups, and chat widgets before the shot; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and responses identify the page verdict and billing status in headers. An MCP server lets AI agents using Claude, Cursor, or another MCP client take screenshots. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Every feature is on every plan. Sign up for 1,000 free screenshots a month, with no card.
10. Frequently asked questions
Does fullPage() also make the browser use a mobile screen?
No. Full-page length and viewport or mobile emulation are separate settings. Choose the layout you intend to capture.
Will a full-page screenshot include content loaded after scrolling?
Not necessarily. Lazy-loaded resources may need the documented wait behavior or a page-specific readiness step. Verify the generated image.
Can the screenshot reflect a particular product variant or location?
It reflects the rendered browser session and page state. Configure the required session and selections before capture, then confirm the resulting page.
Do I need Cloudflare Browser Rendering for Browsershot?
No. It is an option documented for the separate Laravel Screenshot wrapper’s Cloudflare driver. Ordinary Browsershot uses its own documented setup; check the package and version installed in your application.
Where can I confirm the available image options?
Use the official Browsershot image documentation for the version you use.


