How to Screenshot Product Pages on Magento Stores with Playwright
Capture Magento product pages with Playwright in the viewport, as a full page, or by element. Choose reliable output and troubleshoot storefront-specific issues.
Use Playwright to open the Magento storefront’s product URL, wait for the page state your task requires, then call page.screenshot() for the visible viewport or set fullPage: true for the full scrollable page. To capture a particular product image or details block, inspect that storefront’s markup and call locator.screenshot() with a selector that matches it. Magento themes are customizable, so there is no universal product-page selector or readiness signal.
The examples below use JavaScript with Playwright. Replace the example URL and any placeholder selector with the actual storefront URL and a selector you have verified in that page’s DOM. These are generic Playwright examples, not a Magento-specific recipe validated against a particular store.
1. Install Playwright and prepare the capture
In a new project directory, install Playwright and its browser binaries:
npm init -y
npm install playwright
npx playwright install chromium
Save the following as screenshot-product.js. Set PRODUCT_URL to the product’s canonical storefront URL. The script records a viewport image, a full-page image, and—if you provide a real selector—an element image.
const { chromium } = require('playwright');
const PRODUCT_URL = process.env.PRODUCT_URL || 'https://store.example/product-url';
const PRODUCT_SELECTOR = process.env.PRODUCT_SELECTOR;
(async () => {
const browser = await chromium.launch({ headless: true });
try {
const page = await browser.newPage({
viewport: { width: 1440, height: 1000 },
deviceScaleFactor: 1,
});
const response = await page.goto(PRODUCT_URL, {
waitUntil: 'domcontentloaded',
timeout: 60_000,
});
if (response && !response.ok()) {
throw new Error(`Navigation returned HTTP ${response.status()}`);
}
// Optional: wait for a store-specific signal you have verified.
// Example only; do not assume this selector exists on every Magento store.
if (PRODUCT_SELECTOR) {
await page.locator(PRODUCT_SELECTOR).waitFor({ state: 'visible', timeout: 15_000 });
}
await page.screenshot({ path: 'product-viewport.png' });
await page.screenshot({ path: 'product-full.png', fullPage: true });
if (PRODUCT_SELECTOR) {
await page.locator(PRODUCT_SELECTOR).screenshot({ path: 'product-element.png' });
}
} finally {
await browser.close();
}
})().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Run it with:
PRODUCT_URL='https://store.example/product-url' \
PRODUCT_SELECTOR='YOUR_VERIFIED_SELECTOR' \
node screenshot-product.js
Omit PRODUCT_SELECTOR if you only need viewport and full-page captures. The wait in the example runs only when you supply a selector. Inspect the actual page before choosing a selector; a theme update can change its markup.
2. Choose viewport, full-page, or element capture
| Mode | Playwright call | Use it for | Keep in mind |
|---|---|---|---|
| Viewport | page.screenshot({ path: 'view.png' }) |
The initial visible state at a chosen viewport size. | Content below the fold is excluded. The viewport defaults and device scale affect the image, so set them deliberately for repeatable output. |
| Full page | page.screenshot({ path: 'full.png', fullPage: true }) |
A tall image containing the page’s scrollable content. | Very long pages can produce large images and take longer to encode or transfer. A full-page capture does not prove that every lazy-loaded image has finished loading. |
| Element | page.locator('YOUR_SELECTOR').screenshot({ path: 'element.png' }) |
A product image, pricing/details region, or other specific element. | Use a selector verified on this storefront. Playwright scrolls the matching element into view; a scrollable element’s screenshot shows only its currently scrolled content. |
Playwright documents page and locator screenshots, including full-page capture and element capture, in its Screenshots guide, Page API, and Locator API.
3. Select the product element from the actual storefront
Do not copy a selector from an unrelated Magento installation and expect it to work. Product pages may use different themes, extensions, custom templates, and responsive markup. Inspect the target page in browser developer tools or use Playwright’s locator inspection to find a stable element in that page.
Prefer a selector tied to a stable attribute or accessible name over a long chain of layout-dependent classes. Verify that it resolves to exactly the intended element. For example, first check how many elements match:
const count = await page.locator('YOUR_VERIFIED_SELECTOR').count();
if (count !== 1) {
throw new Error(`Expected one product element, found ${count}`);
}
If the selector matches a gallery container with internal scrolling, its locator screenshot captures only the container’s currently visible content. If you need the entire gallery or page, determine whether scrolling or a different element is appropriate for the actual markup.
4. Wait for the state you need
A screenshot shows the rendered state reached by the page when capture runs. Navigation completion alone does not establish that a particular Magento store has loaded its product gallery, selected variant, price, or deferred content. Choose a readiness condition that matches your task and verify it against the storefront.
- Wait for a known element: use
locator.waitFor({ state: 'visible' })only with a selector observed on the target page. - Wait for an explicit delay: a fixed timeout can accommodate a known animation or delayed change, but it adds time and is not a guarantee of readiness.
- Wait for network idle: this can help on pages that become quiet after loading, but persistent analytics or polling can prevent idleness. Do not treat it as a universal Magento signal.
- Lazy-loaded images: full-page mode captures the scrollable page, but the cited Playwright references do not establish that every storefront’s lazy images will load automatically. If missing images matter, inspect the page’s behavior and use a store-specific scroll/readiness approach.
For a screenshot after a verified element appears, place the wait immediately before capture:
const product = page.locator('YOUR_VERIFIED_SELECTOR');
await product.waitFor({ state: 'visible', timeout: 15_000 });
await product.screenshot({ path: 'product-element.png' });
5. Configure image output and repeatability
Playwright’s screenshot options let you choose image format, output destination, capture region, and rendering behavior. Check the documentation for the installed Playwright version because the online references can track a newer version.
| Option | Effect | Practical use |
|---|---|---|
path |
Writes the image to a file; without it, the screenshot result is returned as bytes/buffer. | Use a path for artifacts; use returned bytes for upload or further processing. |
type |
Selects a supported image format such as PNG or JPEG. The file extension does not by itself select the format. | Use PNG for lossless output; JPEG quality can trade image size against fidelity. |
quality |
Sets lossy image quality for applicable formats. | Use only where supported by the selected format; compare output for text and product details. |
fullPage |
Requests capture of the full scrollable page; defaults to false. | Use for a tall page capture rather than only the current viewport. |
clip |
Restricts the capture to a specified rectangle. | Use when a fixed region is needed instead of a DOM element. |
scale |
Controls whether output follows CSS pixel sizing or device pixel sizing. | Set it intentionally when comparing images across runs or devices. |
mask |
Covers selected locators in the screenshot. | Mask dynamic or personally identifying regions in test artifacts where appropriate. |
animations |
Controls animation handling during capture. | Disable or fast-forward animations when they make visual comparisons unstable. |
For repeatable visual checks, keep the viewport, device scale, format, and readiness condition consistent. Mask volatile regions or control animation where appropriate. See the current Page API screenshot options for exact option names and supported values.
6. Return image bytes instead of saving a file
Omit path to get the image as a buffer. The following runnable variation captures the viewport and writes the returned bytes to a file; you can instead pass image to an upload or processing function.
const { chromium } = require('playwright');
const fs = require('node:fs/promises');
(async () => {
const browser = await chromium.launch();
try {
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
await page.goto('https://store.example/product-url', {
waitUntil: 'domcontentloaded',
timeout: 60_000,
});
const image = await page.screenshot({ type: 'png' });
await fs.writeFile('product.png', image);
// Pass image to your own image-processing or upload code as needed.
} finally {
await browser.close();
}
})().catch((error) => {
console.error(error);
process.exitCode = 1;
});
7. Capture a Magento product page with Python
If your project uses Python, install the Playwright package and browser:
python -m pip install playwright
python -m playwright install chromium
Save as screenshot_product.py. Supply a verified selector through the environment if you want an element image.
import os
from playwright.sync_api import sync_playwright
url = os.environ.get("PRODUCT_URL", "https://store.example/product-url")
selector = os.environ.get("PRODUCT_SELECTOR")
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
try:
page = browser.new_page(viewport={"width": 1440, "height": 1000}, device_scale_factor=1)
response = page.goto(url, wait_until="domcontentloaded", timeout=60_000)
if response is not None and not response.ok:
raise RuntimeError(f"Navigation returned HTTP {response.status}")
if selector:
product = page.locator(selector)
product.wait_for(state="visible", timeout=15_000)
page.screenshot(path="product-viewport.png")
page.screenshot(path="product-full.png", full_page=True)
if selector:
page.locator(selector).screenshot(path="product-element.png")
finally:
browser.close()
Run it with PRODUCT_URL='https://store.example/product-url' PRODUCT_SELECTOR='YOUR_VERIFIED_SELECTOR' python screenshot_product.py. The Python locator screenshot API also scrolls the matched element into view; inspect its documentation for version-specific options.
8. cURL and Node.js options outside browser automation
cURL does not render a storefront or operate a browser, so it cannot by itself produce a screenshot from a Magento URL. If you need a direct HTTP screenshot endpoint instead of managing browser installation, use a screenshot API. ScreenshotNeo accepts a URL in one GET request and returns an image or PDF. Its [API documentation](https://screenshotneo.com/docs/) describes the endpoint and options.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://store.example/product-url \
-o product.webp
The equivalent Node.js request using the built-in fetch API is:
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://store.example/product-url',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(({ writeFile }) => writeFile('product.webp', bytes));
For example, a Python request using the same endpoint is:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://store.example/product-url"},
timeout=90,
)
r.raise_for_status()
with open("product.webp", "wb") as f:
f.write(r.content)
9. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. It can capture a URL as PNG, JPEG, WebP, or PDF without you installing and managing a browser for this request. For a product page, the basic request is:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://store.example/product-url -o shot.webp
Cookie banners and consent overlays, newsletter popups, and chat widgets are removed before capture; each step can be turned off. Bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies page verdict and billing in headers. An 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.
See the ScreenshotNeo API docs for request options, then sign up for 1,000 free screenshots a month with no card.
10. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Browser executable missing | The Playwright package is installed but Chromium’s browser binaries are not. | Run npx playwright install chromium for Node.js or python -m playwright install chromium for Python. |
| Navigation times out | The storefront is slow, a request remains active, or the selected navigation condition is not reached. | Check the URL and network access, use a suitable timeout, and select a navigation wait condition that matches the task. Avoid assuming network idle is suitable for every page. |
| Product selector does not match | The selector was copied from another theme, changed, or matches nothing on this page. | Inspect this storefront’s DOM, verify the locator count, and update the selector. Do not rely on a universal Magento class name. |
| Element is not visible | The element is hidden, below a conditional state, or not yet rendered. | Confirm the correct product/variant state and wait for a verified visible element. A longer timeout will not fix an incorrect selector or hidden state. |
| Image is blank or incomplete | The capture ran before content or lazy images rendered, or the element was offscreen/deferred. | Wait on a page-specific readiness condition, inspect lazy-loading behavior, and capture only after the required content is present. |
| Screenshot is unexpectedly tall or truncated | Full-page mode captures the scrollable page; a very long or dynamically growing page can have unexpected dimensions. | Use viewport mode or a clip/element capture if only a region is required; inspect the page dimensions and dynamic content. |
| Images differ between runs | Viewport, device scale, animation, dynamic content, or timing differs. | Set viewport and scale, use stable readiness checks, and consider masking volatile areas or disabling animations. |
| cURL returns an error or non-image response | The endpoint request or API key may be invalid, or the response may indicate a failed capture. | Check the URL, key, HTTP status, and response headers; use the API documentation for supported parameters and interpret the page-verdict and billing headers. |
11. Performance, reliability, and cost
With Playwright, runtime includes browser startup, navigation, readiness waits, rendering, and image encoding. Reuse a browser process for a batch of captures and create separate pages or contexts as needed; always close resources in a finally block. Full-page images can be much larger than viewport images, so prefer a specific element or viewport when that is all the workflow needs. Returning a buffer avoids an intermediate file when the next step uploads or processes the image.
For reliable automation, make viewport and device scale explicit, use a selector verified against the actual storefront, set a finite timeout, and handle navigation failures. Storefront changes, consent dialogs, variant selections, and deferred content can change what appears in the capture; build readiness checks around the visible page behavior required by your use case.
Playwright is open-source browser automation software, but running captures still uses your compute, browser storage, network, and maintenance time. ScreenshotNeo has a free plan with 1,000 shots per month and no card; paid plans are Starter $5 for 3,000, Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000. Yearly billing gives two months free, and every feature is on every plan. Choose based on your expected volume and whether managing the browser is worth the operational cost.
12. FAQ
Can I use this on any Magento store?
The Playwright workflow can navigate to a publicly accessible page, but each storefront may differ in markup, access requirements, and page behavior. Verify selectors and readiness conditions on the specific store.
Can I screenshot just the product photo?
Yes. Inspect the DOM, identify a selector for the image or gallery element you want, and use locator.screenshot(). The selector is storefront-specific.
Will full-page capture load every image below the fold?
Not necessarily. Full-page controls screenshot scope; it does not define a universal lazy-image loading guarantee. Check the rendered page and add a page-specific readiness approach if those images are required.
Can I create screenshots without writing browser automation?
Yes. ScreenshotNeo takes a URL through its screenshot API and also offers an MCP server for AI agents. Its free plan includes 1,000 shots per month without a card.


