ScreenshotNeo

BlogHow-to

How to Capture and Frame Website Screenshots in Nuxt.js

Capture a Nuxt route, full page, or component with Playwright. Learn how to choose the frame, wait for the right page state, and make captures repeatable.

By the ScreenshotNeo team4 October 20268 min read

Use Playwright through Nuxt’s @nuxt/test-utils browser-testing support. Open the route in a browser, wait for the state you want to show, then choose a viewport screenshot, a full-page screenshot, a locator screenshot for one component, or a coordinate-based crop. The examples below use Nuxt 4 documentation and Playwright’s screenshot API; check the documentation for your installed Nuxt and Playwright versions before adapting them.

1. Choose what the screenshot should frame

First decide what the image needs to communicate. A viewport capture shows only what is visible at the chosen browser size. A full-page capture includes content below the fold. A locator capture frames a rendered component, while clip selects a rectangle by page coordinates.

Goal Playwright option Trade-off
Show the visible browser area page.screenshot() Predictable viewport-sized image; content below the fold is omitted.
Show the entire scrollable page page.screenshot({ fullPage: true }) Includes below-the-fold content, producing a tall image.
Show one DOM component page.locator(selector).screenshot() Frames the element’s rendered bounds; requires a locator that identifies the intended element.
Crop a fixed rectangle page.screenshot({ clip: { x, y, width, height } }) Coordinates and dimensions must match the intended page region.

For a target that already exists in the DOM, a locator is usually easier to maintain than fixed coordinates. Use a coordinate clip when the crop itself is the requirement. These controls are documented in the Playwright Page API and screenshot guide.

2. Set up Nuxt browser testing

Nuxt’s Nuxt 4 testing documentation describes browser testing powered by Playwright, the createPage helper, and integration with the Playwright test runner through @nuxt/test-utils/playwright. Use the runner setup that matches your repository and install the required packages according to the Nuxt testing documentation.

A representative test-runner file, for example tests/nuxt-screenshot.spec.ts, looks like this:

import { expect, test } from '@nuxt/test-utils/playwright'

test('capture a Nuxt route', async ({ page, goto }) => {
  await goto('/', { waitUntil: 'hydration' })
  await expect(page.locator('main')).toBeVisible()
  await page.screenshot({ path: 'artifacts/home.png' })
})

The exact setup can depend on the project’s Nuxt root and test-runner configuration. Nuxt documents configuring its root directory in the Playwright integration. Ensure the output directory exists or create it in your test setup, and use a route that is available in the test environment.

Nuxt’s example waits for hydration before checking page content. That is a useful starting point, not a universal readiness rule: a route may still be loading data, fonts, images, or lazy content. Wait for the actual state the image should represent.

3. Capture a route, page, component, or crop

Viewport screenshot

await page.screenshot({ path: 'artifacts/viewport.png' })

With no full-page option, Playwright captures the current viewport. Set the viewport in the browser or test configuration if the screenshot needs consistent dimensions; keep it consistent between captures you intend to compare.

Full-page screenshot

await page.screenshot({
  path: 'artifacts/full-page.png',
  fullPage: true,
})

This captures the full scrollable page. It can be useful for reviewing a route as one image, but the result may be very tall. A full-page image is not equivalent to a single viewport: content that appears only after scrolling or other interaction may need additional page-specific handling.

Component screenshot

const card = page.locator('[data-testid="pricing-card"]')
await expect(card).toBeVisible()
await card.screenshot({ path: 'artifacts/pricing-card.png' })

Prefer a stable selector, such as a test ID, when available. A locator screenshot captures the element’s rendered bounds, so check that the locator resolves to the intended element and that the element is visible in the desired state.

Coordinate crop

await page.screenshot({
  path: 'artifacts/crop.png',
  clip: { x: 40, y: 80, width: 720, height: 420 },
})

The clip rectangle uses page coordinates. If the layout, viewport, or scroll position changes, fixed coordinates may frame a different region. Use a locator screenshot when the region is naturally represented by a DOM element.

4. Make captures representative and repeatable

Wait for the intended route state

Navigate to the route and wait for a meaningful signal: a visible heading, a loaded component, or an application-specific ready marker. For example:

await goto('/reports', { waitUntil: 'hydration' })
await expect(page.getByRole('heading', { name: 'Reports' })).toBeVisible()
await expect(page.locator('[data-testid="report-chart"]')).toBeVisible()
await page.screenshot({ path: 'artifacts/reports.png' })

Do not assume navigation alone means every async request has completed. If the capture includes remote data, wait for the relevant rendered result. For lazy images or content that appears after scrolling, trigger the behavior the page needs before taking the screenshot, then wait for the target to be visible. The right readiness check depends on the route; Nuxt and Playwright do not prescribe one universal wait recipe for every application.

Control output scale

Playwright’s scale option supports 'css' and 'device'. CSS scale gives one output pixel per CSS pixel and tends to produce smaller, more consistent dimensions. Device scale preserves device-pixel output based on the device pixel ratio and can produce larger images. Choose based on where the image will be used, and verify the resulting dimensions when using high-DPI output.

await page.screenshot({
  path: 'artifacts/css-pixels.png',
  scale: 'css',
})

await page.screenshot({
  path: 'artifacts/device-pixels.png',
  scale: 'device',
})

Keep dynamic content understandable

For visual documentation or stable review images, you can use screenshot style overrides to adjust or hide dynamic content, or locator screenshot masking to cover selected regions. These change what the screenshot shows. Explain any styling or masking when the image is presented as evidence, documentation, or a review artifact, especially when it affects interpretation or privacy. See the Playwright Page API for the supported options.

Remember what Nuxt is rendering

Nuxt supports universal, client-side, hybrid, and edge rendering patterns; universal rendering is the default and returns server-rendered HTML to the browser. Route rules can select rendering behavior or caching strategies. A screenshot is the browser’s rendering of a particular route and state. Use browser capture when the goal is the interface and its client-side behavior; an HTML response check answers a different question. See Nuxt’s documentation on rendering modes.

5. Options and framing decisions

  • Viewport: Capture the visible browser area when a compact, fixed-size preview is the goal. Fix the viewport for repeatability.
  • fullPage: true: Include the whole scrollable document when below-the-fold content matters. Expect a taller output.
  • Locator screenshot: Capture a component’s rendered bounds. Check that the selector is specific and resolves to the intended visible element.
  • clip: Specify x, y, width, and height for a rectangular crop. Keep page layout and viewport stable if reusing coordinates.
  • scale: 'css': One output pixel per CSS pixel; useful when consistent CSS-pixel dimensions and smaller files are preferred.
  • scale: 'device': Device-pixel output; useful when preserving pixel density matters, with potentially larger images.
  • style and masks: Adjust or cover selected page regions for a capture, and disclose alterations where context matters.

These options are described in the Playwright API. Nuxt’s testing guide covers its browser-testing integration.

6. Troubleshooting common screenshot problems

Symptom Likely cause What to do
Screenshot shows a loading state The route navigated, but its async data or client rendering is not ready. Wait for a route-specific visible result or readiness marker before capturing.
Component screenshot fails or captures the wrong region The locator is missing, ambiguous, hidden, or identifies a different element than intended. Use a stable, specific selector and assert that the target is visible before the screenshot.
Below-the-fold content is missing The default screenshot covers only the viewport. Set fullPage: true, or capture a specific component if only one region is needed.
Crop is shifted or empty The clip coordinates no longer match the page layout or the intended rectangle. Recheck the viewport and coordinates; prefer a locator screenshot for a DOM element.
Fonts or images look incomplete The capture started before those assets or dependent content were ready. Wait for the actual font, image, or rendered-content condition required by the route.
Image dimensions are unexpectedly large scale: 'device' can produce device-pixel dimensions. Use scale: 'css' if CSS-pixel output is the intended size.
Capture works locally but not in CI The route may depend on environment-specific state, credentials, network resources, or timing. Check the test environment and route readiness; make the screenshot’s viewport and application state explicit.

7. Performance, reliability, and cost

Browser screenshots require opening and rendering the route, so the capture time depends on the page’s own navigation, client work, and assets. Avoid waiting for a broad condition when a precise route-specific signal is enough, but do not capture before the required state is ready. Full-page images and device-scale output can create larger files; use viewport or element framing and CSS scale when those meet the image’s purpose.

For repeatable results, keep route, viewport, rendering state, and readiness checks consistent. Remote content, authentication, fonts, and lazy loading can vary by environment. Nuxt’s rendering mode and route rules can affect what the browser receives, so diagnose the rendered route rather than assuming every route behaves like a static page. Playwright and Nuxt documentation do not provide a universal capture-time or cost benchmark; actual runtime and infrastructure cost depend on your setup.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns a PNG, JPEG, WebP, or PDF, and its options include full-page capture, CSS-selector element capture, custom viewport and device presets, and lazy-image loading. See the ScreenshotNeo site and 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}`)
await Bun.write('shot.webp', res)

Replace YOUR_API_KEY with your API key and set url to the Nuxt route you want to capture. ScreenshotNeo removes cookie and consent banners, 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 response headers report the page verdict and billing status. Its MCP server offers take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots.

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

FAQ

Does a Nuxt screenshot capture server-rendered HTML or the browser interface?

Playwright captures the browser-rendered route and state. That includes what the browser displays after the application runs, rather than only checking the server’s returned HTML.

Should I use a full-page screenshot for every route?

No. Use it when the whole scrollable document is useful in one image. A viewport or component capture is often a better fit for previews and focused review.

When should I choose CSS scale?

Choose scale: 'css' when output dimensions should correspond to CSS pixels. Choose device scale when device-pixel detail is needed and larger dimensions are acceptable.

Can I make screenshots stable when the page has changing content?

Wait for the intended application state, and consider Playwright’s screenshot style overrides or locator masks for dynamic regions. Make alterations clear when the capture is used to explain or verify the page.

Sources