How to capture screenshots of a Shopify storefront with Playwright
Capture a Shopify storefront with Playwright, choose viewport or full-page output, and make screenshots more repeatable with practical options and troubleshooting.
Use Playwright’s page.screenshot() to save a Shopify storefront as an image. Navigate to the storefront URL, then capture the visible viewport or set fullPage: true to include the scrollable page. The example below uses Node.js and Chromium; replace the example URL with a storefront you are authorized to access.
1. Install Playwright
Create a project and install Playwright:
mkdir shopify-screenshot
cd shopify-screenshot
npm init -y
npm install playwright
npx playwright install chromium
Save the following as screenshot.js. It writes a viewport screenshot to storefront.png and closes the browser even if navigation or capture fails.
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
try {
const context = await browser.newContext({ viewport: { width: 1440, height: 1000 } });
const page = await context.newPage();
await page.goto('https://your-storefront.example', {
waitUntil: 'load',
timeout: 30000,
});
await page.screenshot({ path: 'storefront.png' });
} finally {
await browser.close();
}
})();
Run it with node screenshot.js. The storefront hostname is deliberately an example; substitute the actual public storefront URL. This workflow does not assume a universal Shopify password page, consent prompt, or regional storefront state.
2. Choose viewport or full-page capture
By default, Playwright captures the current viewport. To include the page’s scrollable content, pass fullPage: true:
await page.screenshot({
path: 'storefront-full.png',
fullPage: true,
});
Use a viewport capture when you need the initial visible screen or a fixed-size image. A full-page capture is useful for reviewing a long landing page, but its output may be very tall. Sticky elements and content that changes while the page loads can also make the resulting image less suitable for a pixel comparison.
3. Set format, dimensions and scale
Playwright supports PNG, JPEG and WebP screenshot output. The file extension can determine the format; you can also set type explicitly. JPEG supports a quality value, while PNG is useful when you need lossless output. The scale option controls whether output pixels follow CSS pixels or device pixels.
// PNG, one output pixel per CSS pixel
await page.screenshot({ path: 'storefront.png', type: 'png', scale: 'css' });
// JPEG at a chosen quality
await page.screenshot({ path: 'storefront.jpg', type: 'jpeg', quality: 85 });
// WebP
await page.screenshot({ path: 'storefront.webp', type: 'webp', quality: 85 });
// Higher-resolution output using device pixels
await page.screenshot({ path: 'storefront-retina.png', scale: 'device' });
Choose CSS scale for predictable CSS-sized output. Device scale can preserve more pixel detail on high-density displays and produce larger files. Set the viewport in the browser context when the screenshot must have a particular visible width and height; fullPage changes the capture height to include scrollable content.
4. Make the capture more repeatable
For repeatable captures, keep the browser, viewport and capture settings consistent. Wait for a meaningful page condition when the storefront has content that appears after initial navigation. For example, wait for a selector that is expected on the target page:
await page.goto('https://your-storefront.example', { waitUntil: 'load' });
await page.locator('main').waitFor({ state: 'visible', timeout: 10000 });
await page.screenshot({ path: 'storefront.png', fullPage: true });
Use a selector that exists on the storefront you are capturing; main is only an example. If no reliable selector is available, a short, bounded delay can allow late content to appear, but it does not prove the page is fully settled.
Screenshot options can reduce visual noise: disable animations, mask selected locators, or apply a stylesheet at capture time. These controls help with changing or distracting content, but do not guarantee identical pixels across runs.
await page.screenshot({
path: 'storefront-stable.png',
fullPage: true,
animations: 'disabled',
mask: [page.locator('.dynamic-price')],
style: '.newsletter-popup { visibility: hidden !important; }',
});
Replace the example selector and stylesheet with ones appropriate to the page. Avoid hiding content if the screenshot is intended to document the storefront as visitors see it.
For visual regression comparisons, use the same environment for generating and comparing images. Playwright documents that rendering can vary with the host operating system, browser version, settings, hardware, power source and headless mode. Its screenshot assertions wait for consecutive screenshots to match before comparing against the expected image, which can help when using its test runner.
5. Capture a storefront with Python or cURL
Playwright’s browser workflow is shown above in Node.js. If your automation stack uses Python, the official Playwright Python package provides the same browser steps:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
try:
page = browser.new_page(viewport={"width": 1440, "height": 1000})
page.goto("https://your-storefront.example", wait_until="load", timeout=30000)
page.screenshot(path="storefront.png", full_page=True)
finally:
browser.close()
Install it with pip install playwright and playwright install chromium. Replace the example URL with the storefront you are authorized to access.
cURL does not run Playwright or launch a browser, so it is not a cURL command for the DIY Playwright workflow. If you want a direct screenshot API request instead, see the ScreenshotNeo option below.
6. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| Browser executable is missing | The Playwright package is installed, but its browser has not been installed in this environment. | Run npx playwright install chromium, or playwright install chromium for Python. |
| Navigation times out | The page did not reach the selected navigation state before the timeout, or the host is slow or unreachable. | Check the URL and network access. Choose a suitable navigation wait condition and set a bounded timeout. Do not treat a longer timeout as proof that the page loaded correctly. |
| The screenshot is blank or incomplete | The capture may happen before the relevant content appears, or the target page may not be the expected storefront state. | Inspect the page URL and visible state, then wait for a real page selector before capture. Storefront access and regional behavior can vary; do not assume one state applies to every Shopify store. |
| Content below the fold is missing | The default screenshot covers only the viewport. | Pass fullPage: true. |
| Image files are unexpectedly large | Full-page output or device-pixel scale can produce many pixels; PNG is lossless. | Use viewport capture if sufficient, CSS scale for CSS-sized output, or JPEG/WebP with an appropriate quality setting when lossy compression is acceptable. |
| Visual comparisons are flaky | Dynamic content or differences between browser and machine environments affect rendering. | Keep the environment and viewport consistent; wait for a stable page condition and consider disabling animations, masking dynamic regions, or applying a capture stylesheet. |
7. Performance, reliability and cost
Launching a browser has setup and resource costs, so for repeated captures reuse a browser process where your application architecture permits, while creating a fresh page or context when you need isolation. Full-page screenshots process more content than viewport captures, and device-scale images may contain more pixels. Those choices affect capture time, memory and output size; measure them in the environment where your job will run rather than relying on a universal timing estimate.
For reliable automation, close browsers in a finally block, set bounded navigation and selector waits, and record the URL and capture settings alongside the output when debugging. A successful screenshot call only means an image was written; it does not establish that the page showed the intended storefront content.
The DIY method has no per-shot API fee, but you manage browser installation, compute, storage and maintenance. A screenshot API shifts browser management to a service and may charge according to its own plan and billing rules. Compare the operational work and billing behavior that matter to your use case.
8. Or skip the browser setup
ScreenshotNeo is a website screenshot API: one GET request returns an image or PDF. Its API accepts common screenshot parameter names, which can make switching straightforward. See the ScreenshotNeo API documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://your-storefront.example -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://your-storefront.example"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://your-storefront.example',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo removes cookie banners, newsletter popups and chat widgets before capture. Bot checks, blank pages and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Response headers indicate the page verdict and billing status.
Sign up for ScreenshotNeo’s free plan to get 1,000 screenshots a month with no card.
9. FAQ
Can I capture a Shopify storefront I do not own?
Use the workflow only for storefronts you are authorized to access, and respect the site’s access controls and applicable terms.
Does a full-page screenshot include every lazy-loaded image?
fullPage: true captures the scrollable page, but the cited API description does not establish that every storefront’s lazy-loaded content will have loaded. Wait for relevant content or use a workflow designed for the page’s behavior.
Will two screenshots always be pixel-identical?
No. Browser and machine differences, along with dynamic page content, can change rendered pixels. Keep the environment consistent and stabilize changing regions when using visual comparisons.


