ScreenshotNeo

BlogHow-to

Playwright Screenshot Full Page with Custom CSS

Capture a full-page Playwright screenshot with temporary CSS, or use a stylesheet file for visual tests. Includes runnable JavaScript, options, and fixes.

By the ScreenshotNeo team4 October 20266 min read

Use fullPage: true to capture the whole scrollable page, and pass CSS text in style to apply temporary styling during capture:

const screenshot = await page.screenshot({
  path: 'full.png',
  fullPage: true,
  style: `
    .cookie-banner,
    .chat-widget {
      display: none !important;
    }
  `,
});

The stylesheet affects the screenshot capture; it does not permanently change the page. For a Playwright Test visual assertion, use toHaveScreenshot() with stylePath pointing to a CSS file. The direct API saves the image when given path and also returns its image buffer.

1. Set up a runnable Playwright script

In a new Node.js project, install Playwright and its browser:

npm init -y
npm install playwright
npx playwright install chromium

Save this as screenshot.mjs and run it with node screenshot.mjs:

import { chromium } from 'playwright';

const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });

try {
  await page.goto('https://example.com', { waitUntil: 'networkidle' });
  await page.screenshot({
    path: 'full.png',
    fullPage: true,
    style: `
      .cookie-banner,
      .newsletter-modal,
      .chat-widget {
        display: none !important;
      }
    `,
  });
} finally {
  await browser.close();
}

Replace the example URL and selectors with the page and elements you want to capture. The style value is stylesheet text, not a path.

2. Apply custom CSS to a direct screenshot

page.screenshot() is the direct capture API. Combine fullPage and style when you need the entire document with temporary visual changes:

await page.screenshot({
  path: 'full.png',
  fullPage: true,
  style: `
    .volatile-ad { visibility: hidden !important; }
    .timestamp { display: none !important; }
  `,
});

Prefer narrowly scoped selectors. Hiding content changes what the screenshot shows, so use it only when removing that content is appropriate for the capture. For example, hiding an embedded frame may reduce visual variation, but it also removes the frame from the image.

CSS can also adjust the page for legibility, such as removing a sticky header or changing a background. Since the styles apply for screenshot capture, they are useful for one-off output without editing the site itself.

3. Use a CSS file with Playwright Test

For visual regression checks, keep screenshot styles in a file and pass its path to Playwright Test’s toHaveScreenshot() assertion. Create screenshot.css:

.cookie-banner,
.chat-widget {
  display: none !important;
}

Then use the assertion in a Playwright Test test file:

import { test, expect } from '@playwright/test';

test('page visual baseline', async ({ page }) => {
  await page.goto('https://example.com');
  await expect(page).toHaveScreenshot('full.png', {
    fullPage: true,
    stylePath: './screenshot.css',
  });
});

toHaveScreenshot() is provided by Playwright Test; it is not the same as calling page.screenshot(). The assertion captures and compares against a stored expectation, retrying until consecutive screenshots stabilize before comparison. Use the direct API when you want an image file or buffer without an assertion.

4. Choose page, element, and assertion options

Goal Method or option What it does
Capture the scrollable document page.screenshot({ fullPage: true }) Captures beyond the current viewport.
Capture only the visible viewport Omit fullPage or set it to false Limits the capture to the current viewport.
Capture a particular element Locator screenshot methods Targets the element area instead of the whole document.
Apply inline styles for a direct capture style: 'CSS text' Temporarily injects CSS for the screenshot.
Apply a stylesheet in an assertion stylePath: './file.css' Uses a CSS file with Playwright Test screenshot assertions.

For assertions, other controls include animation handling, caret behavior, masks, scale, and pixel-difference tolerances. Pick them based on what the test should detect. A mask can cover a known dynamic area; a tolerance can account for small rendering differences. Broad tolerances can also hide changes the test ought to catch.

The screenshot style and assertion stylePath options are documented as added in Playwright v1.41. Check the API for the version installed in your project if either option is unavailable.

5. Make full-page captures reliable

  • Wait for the page content you need. Choose an appropriate navigation wait condition, then wait for a meaningful selector or app-ready signal if the page renders content after navigation.
  • Account for lazy-loaded content. A full-page capture covers the document, but pages that load images or sections only after scrolling may need an explicit scroll-and-wait step before capture.
  • Keep screenshot environments aligned. Operating system, browser version, settings, hardware, power source, and headless mode can affect visual output. Use the same environment for baseline creation and comparison.
  • Use stable CSS selectors. Prefer selectors tied to meaningful classes or attributes over fragile positional selectors.
  • Keep page size in mind. Very long pages produce large images and can take longer to capture and compare. Capture an element or viewport instead when the test only concerns a smaller area.

6. Troubleshooting

Symptom Likely cause Fix
style has no effect A filename was passed where CSS text is expected, or the selector does not match. Pass the CSS contents as a string and verify the selector against the page.
stylePath is rejected or ignored The call is not a Playwright Test screenshot assertion, the path is wrong, or the installed release predates v1.41. Use toHaveScreenshot(), correct the file path, and check the installed Playwright version.
Only the viewport appears fullPage: true was omitted, or an element screenshot method was used. Use page.screenshot({ fullPage: true }) for the full scrollable document.
Full-page image is blank or content is missing The page may not have finished rendering, or lazy content may not have loaded. Wait for the relevant content and, where necessary, scroll through the page to trigger lazy loading before capture.
Visual assertion fails on a different machine Browser, operating system, rendering settings, hardware, or headless mode may differ from the baseline environment. Align the environments and regenerate the baseline only when the visual change is intended.
Screenshot assertion never stabilizes Animated or continuously changing content may differ between consecutive captures. Disable or mask the specific dynamic region using assertion options, or hide it with a focused screenshot stylesheet.
Capture takes too long or the image is unwieldy The page is exceptionally long or resource-heavy. Wait only for required content, reduce the capture scope to the relevant element or viewport, or avoid repeatedly capturing the full document in a tight loop.

7. Performance, repeatability, and cost

A full-page screenshot needs to render and encode a potentially tall image. Longer pages and large assets can increase capture time, memory use, and output size. For recurring visual checks, save only the captures needed for comparison and keep the browser and machine configuration consistent.

Playwright is a browser automation library that you run in your own environment; this workflow does not charge a per-screenshot API fee. Account for the compute and storage used by your own machines or CI. Visual assertion stabilization helps with transient rendering, but it cannot make unlike environments pixel-identical.

8. Or skip the browser setup

If you need a clean screenshot without maintaining browser automation, ScreenshotNeo is a website screenshot API and MCP server for developers. Send one GET request with a URL and receive PNG, JPEG, WebP, or PDF. Its capture can accept cookie and consent banners like a visitor and remove 60+ known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers say the page verdict and whether the request was billed. An MCP server offers take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. 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}`);

Create a free ScreenshotNeo account for 1,000 screenshots a month with no card.

9. FAQ

Does screenshot CSS change the live website?

No. The custom styles are applied for the screenshot operation, not saved as changes to the website.

Can I use a CSS filename with page.screenshot()?

Not through the style option: it takes CSS text. The stylePath option is for Playwright Test screenshot assertions.

Does a full-page screenshot capture a specific element?

No. Full-page capture targets the scrollable document. Use a locator or element screenshot method when the target is one element.

Will two machines always produce identical screenshots?

No. Rendering can vary with the OS, browser version, settings, hardware, power source, and headless mode. Align environments for visual comparisons.

Where can I check the option syntax?

Use the official Playwright screenshot guide and page screenshot API; verify compatibility with your installed release.