How to Capture a Screenshot of a Vue Page with Playwright
Use Playwright to capture a Vue page as a viewport, full-page, or element screenshot, with runnable setup and practical fixes for common issues.
To capture a Vue page with Playwright, navigate a Playwright Page to the URL where your Vue app is running, then call await page.screenshot({ path: 'screenshot.png' }). That captures the visible viewport. Set fullPage: true for the full scrollable page, or call screenshot() on a locator to capture one component. The screenshot API is the same for Vue as for any web page.
1. Install Playwright and start your Vue app
Run the Vue development server in one terminal, then use the URL it prints. Vite commonly serves a local app at http://localhost:5173; use your actual URL and port if they differ.
npm install -D playwright
npx playwright install chromium
Save the following as screenshot.js. It launches Chromium, visits the Vue app, saves a viewport screenshot, and closes the browser even if navigation or capture fails.
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
try {
const page = await browser.newPage({
viewport: { width: 1440, height: 900 },
deviceScaleFactor: 1,
});
await page.goto('http://localhost:5173', {
waitUntil: 'networkidle',
timeout: 30_000,
});
await page.screenshot({ path: 'vue-page.png' });
} finally {
await browser.close();
}
})().catch(error => {
console.error(error);
process.exitCode = 1;
});
Execute it with node screenshot.js. The networkidle wait is suitable for many apps but can hang on pages that keep requests open, such as apps using polling or server-sent events. In those cases, use waitUntil: 'load' or 'domcontentloaded', then wait for the particular UI state your screenshot needs.
2. Choose viewport, full-page, or element capture
| Need | Code | What it captures |
|---|---|---|
| Visible screen | page.screenshot({ path: 'view.png' }) |
The current viewport; this is the default. |
| Entire scrollable page | page.screenshot({ path: 'full.png', fullPage: true }) |
The full page vertically, beyond the current viewport. |
| One Vue component | page.locator('.product-card').screenshot({ path: 'card.png' }) |
The element matched by the locator. |
Prefer a locator for component screenshots. It waits for the target element to be actionable for the screenshot and avoids the discouraged ElementHandle screenshot pattern. Make the selector specific enough to match one intended component.
await page.locator('[data-testid="dashboard-summary"]')
.screenshot({ path: 'summary.png' });
A locator screenshot captures the element, not the entire page around it. If the element is outside the viewport, Playwright can scroll it into view for capture. Add a stable test identifier to Vue markup when class names are likely to change:
<section data-testid="dashboard-summary">
...
</section>
3. Set the viewport and image scale
Viewport size affects responsive layout, so configure it before navigation when the screenshot should represent a particular device or breakpoint. A browser context can also be configured once and reused across pages.
const context = await browser.newContext({
viewport: { width: 390, height: 844 },
deviceScaleFactor: 2,
});
const page = await context.newPage();
For screenshot output, scale: 'css' means one image pixel per CSS pixel. scale: 'device' captures device pixels and can produce a larger image on high-density emulation. A larger viewport or device scale increases pixel count and file size.
await page.screenshot({
path: 'vue-page.png',
scale: 'css',
});
When using context emulation, set the context’s viewport and screen values together if you need finer control over how the browser reports screen dimensions. Playwright notes that setting a page viewport also resets screen size.
4. Wait for Vue content to be ready
Vue may render after the initial document navigation, especially when it fetches data or lazy-loads components. Instead of relying on an arbitrary delay, wait for an element that indicates the page is ready:
await page.goto('http://localhost:5173', { waitUntil: 'domcontentloaded' });
await page.locator('[data-testid="page-ready"]').waitFor({ state: 'visible' });
await page.screenshot({ path: 'ready.png' });
For a chart, image, or asynchronously populated component, wait for that component or its final state. A fixed delay can be useful for a known animation or third-party widget, but it is less reliable because load times vary.
5. Make screenshots more repeatable
Animation, changing timestamps, randomized content, and environment differences can make captures vary between runs. For a locator screenshot, disable animations:
await page.locator('.hero').screenshot({
path: 'hero.png',
animations: 'disabled',
});
Playwright fast-forwards finite animations and cancels infinite animations for this capture behavior. For visual regression checks, Playwright Test offers expect(page).toHaveScreenshot(). Keep browser version, operating system, fonts, viewport, and other rendering conditions consistent between baseline generation and comparison: rendering can differ across machines and runtime settings.
6. Screenshot options worth knowing
| Option | Use |
|---|---|
path |
Writes the screenshot to a file. The extension determines image type; PNG is the default if type is not specified. |
fullPage |
For page screenshots, capture beyond the viewport. Defaults to false. |
type |
Choose 'png' or 'jpeg' when returning image bytes or when you want to make the type explicit. |
quality |
JPEG quality from 0 to 100; only applies to JPEG. |
omitBackground |
Hide the default white background where transparency is supported, useful for PNG. |
scale |
Use 'css' or 'device' output pixel scaling. |
timeout |
Set the maximum time allowed for the screenshot operation. |
animations |
For locator screenshots, disable animations to reduce motion variation. |
For a JPEG screenshot, set a path ending in .jpg or pass type: 'jpeg'. Avoid a lossy format for pixel-exact visual comparisons.
7. Complete Python and cURL alternatives
Playwright’s API is available in multiple languages. If you prefer Python, install the package and browser, then use its synchronous API:
pip install playwright
playwright install chromium
from pathlib import Path
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": 900})
page.goto("http://localhost:5173", wait_until="domcontentloaded")
page.locator("[data-testid='page-ready']").wait_for(state="visible")
page.screenshot(path="vue-page.png", full_page=True)
finally:
browser.close()
cURL alone cannot render a Vue application because the screenshot requires a browser engine to execute JavaScript and lay out the page. Use cURL to call a screenshot service that runs a browser for you. For example, the ScreenshotNeo API accepts a URL and returns an image:
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://your-vue-site.example \
-o vue-page.webp
8. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Connection refused or blank screenshot | The Vue dev server is stopped or the URL/port is wrong. | Start the app and navigate to the exact local URL shown by the server. |
| Screenshot happens before content appears | The initial document loaded but Vue data or components are still pending. | Wait for a locator that marks the content as ready; avoid guessing with a short fixed sleep. |
networkidle never completes |
The app maintains background network requests. | Use domcontentloaded or load, then explicitly wait for the content required in the shot. |
| Locator screenshot times out | The selector does not match, matches hidden content, or the component never renders. | Check the selector and state; wait for visible and verify the element exists. |
| Browser executable is missing | The Playwright package is present but Chromium was not installed. | Run npx playwright install chromium or the Python equivalent. |
| Unexpected differences between runs | Animations, timestamps, fonts, viewport, browser version, or host rendering differ. | Disable animations when suitable and capture comparisons in a consistent environment. |
| Full-page capture omits expected content | Content is loaded only when scrolled into view, or a nested scrolling region owns the content. | Trigger the relevant scrolling/loading behavior first; for an inner panel, capture that locator instead. |
9. Performance, reliability, and cost
A local Playwright capture has no per-screenshot API charge, but your process must install and run a browser and provide the compute, storage, and maintenance. Reuse a browser for batches of pages and create separate contexts when you need isolated cookies or different device settings. Always close pages, contexts, and the browser so repeated jobs do not accumulate resources.
Capture only what you need: a viewport is usually smaller and quicker to write than a long full-page image; element captures are useful for component artifacts. High device scale and full-page dimensions increase image pixels, memory use, and output size. For reliable automation, use explicit readiness conditions, set practical navigation and screenshot timeouts, and retry only transient failures rather than masking a consistently broken page.
10. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. A single GET request can capture a public Vue URL as PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
require('node:fs').writeFileSync('shot.webp', image);
Cookie banners are accepted like a visitor and removed along with known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be disabled. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server gives AI agents tools for screenshots, page information, and PDFs. The free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000. Every feature is available on every plan.
Create a free ScreenshotNeo account and get 1,000 screenshots a month with no card.
11. FAQ
Does Vue need a special screenshot plugin?
No. Playwright captures the rendered browser page, so the same Page screenshot methods work with Vue, React, or plain HTML.
Can I save the screenshot as a buffer instead of a file?
Yes. Omit path from page.screenshot() and use its returned bytes in your script.
Should I capture a viewport or the full page?
Use viewport capture for a device-sized image or visual baseline. Use full-page capture for a document-like artifact. Choose a locator when the deliverable is one component.
Will the screenshot look identical on every computer?
Not necessarily. Browser version, operating system, fonts, hardware, and headless rendering can affect pixels, so keep the capture environment stable for comparisons.


