ScreenshotNeo

BlogHow-to

Puppeteer Screenshot with a Custom Page Background Color

Set a solid screenshot background by injecting CSS before capture. Learn when to use omitBackground, how to save the image, and how to handle common rendering issues.

By the ScreenshotNeo team4 October 20267 min read

To give a Puppeteer screenshot a custom solid background, apply a CSS background color to the rendered page before calling page.screenshot(). Puppeteer’s omitBackground option has a different purpose: it hides the default white background so the screenshot can be transparent; it does not choose a color.

await page.addStyleTag({
  content: `html, body { background-color: #eaf2ff !important; }`,
});
await page.screenshot({ path: 'page.png' });

This works when html or body paints the visible background. If the page uses a nested app shell, canvas, or overlay for its background, apply the style to that element instead. For Puppeteer API details, see the Puppeteer screenshot guide and the Page API.

1. Install Puppeteer and capture a page

For a new Node.js project, install Puppeteer with npm. The package downloads a compatible browser by default. If your project already manages Chrome separately, follow its browser setup and point Puppeteer at that executable as appropriate.

npm install puppeteer

Save the following as screenshot.mjs, then run node screenshot.mjs. It navigates to a URL, waits for the document load event, adds the background rule, and writes a PNG.

import puppeteer from 'puppeteer';

const url = process.argv[2] ?? 'https://example.com';
const browser = await puppeteer.launch({ headless: true });

try {
  const page = await browser.newPage();
  await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });
  await page.goto(url, { waitUntil: 'load', timeout: 30_000 });

  await page.addStyleTag({
    content: `html, body { background-color: #eaf2ff !important; }`,
  });

  await page.screenshot({ path: 'page.png', fullPage: true });
} finally {
  await browser.close();
}

Use it with a target URL, for example node screenshot.mjs https://example.com. The example uses fullPage: true to capture the full scrollable page. Remove that option or set it to false to capture only the current viewport. Puppeteer documents both page screenshots and element screenshots in its Screenshots guide.

2. Make sure the CSS reaches the visible background

The CSS declaration is what sets the color. !important helps it win against ordinary author rules, but it cannot make the wrong element paint the page. A site may set its background on an app root, a full-screen wrapper, or another element.

Use a more specific selector when needed

Inspect the page’s DOM and computed styles to find the element whose background is visible. Then target that element:

await page.addStyleTag({
  content: `
    html, body, #app, .page-shell {
      background-color: #eaf2ff !important;
    }
  `,
});

Replace #app and .page-shell with selectors that actually exist on the target page. Avoid broad rules that color every element unless that is the intended result; they can obscure cards, panels, and other surface colors.

Apply the style after navigation and app rendering

For client-rendered applications, the initial document load may happen before the app has mounted or before its final layout appears. Wait for a stable selector that identifies the rendered page, then inject the CSS:

await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30_000 });
await page.waitForSelector('#app main', { timeout: 15_000 });
await page.addStyleTag({
  content: `html, body, #app { background-color: #eaf2ff !important; }`,
});
await page.screenshot({ path: 'page.png', fullPage: true });

Choose a selector that is meaningful for the site. If the application replaces the relevant DOM after your style is inserted, wait for that render first or insert the style again immediately before capture.

3. Solid color versus transparency

Desired result How to get it
Solid custom color Set background-color in page CSS before capture.
Transparent background Use omitBackground: true when the page content and output format support transparency.
Default page background Do not override the page CSS; leave omitBackground unset or false.

omitBackground is not a color picker. The Puppeteer 25.12.0 API reference describes it as hiding the default white background to allow transparency, with a default of false. Check the documentation for the version installed in your project if its behavior or supported options differ.

// Solid color: CSS sets the background.
await page.addStyleTag({ content: 'html, body { background: #eaf2ff !important; }' });
await page.screenshot({ path: 'solid.png' });

// Transparency: omit the default background instead of choosing a color.
await page.screenshot({ path: 'transparent.png', omitBackground: true });

4. Capture only one element

For a component or card, locate it and use its element screenshot method. Apply the color to the element that paints the area you want captured, or to the page if the element is transparent and inherits the visible page background.

const card = await page.waitForSelector('.report-card');
if (!card) throw new Error('Report card was not found');

await page.addStyleTag({
  content: `.report-card { background-color: #eaf2ff !important; }`,
});
await card.screenshot({ path: 'report-card.png' });

The selector is site-specific. A missing element causes waitForSelector to time out; verify the selector against the rendered DOM and wait for the application to mount.

5. Other output and capture options

The core choice remains the same across capture shapes: change the rendered CSS before capture, then choose the relevant screenshot options.

  • Viewport or full page: set the viewport with page.setViewport(); use fullPage: true for the full scrollable document.
  • File path: pass path to save the screenshot, as in the examples. Without it, page.screenshot() returns image data.
  • Image type: use the screenshot options for PNG, JPEG, or WebP supported by your installed Puppeteer version. JPEG does not preserve transparency; use a format with alpha when transparency matters.
  • Retina-sized output: set deviceScaleFactor on the viewport before navigation or capture. This affects output pixel dimensions and file size.
  • Transparent output: use omitBackground: true; do not expect it to set a solid color.

Consult the version-matched ScreenshotOptions reference for available option names and defaults. The page-level screenshot method is documented at Page.screenshot().

6. Troubleshooting

Symptom Likely cause Fix
The screenshot is still white The visible background is painted by another element, or a later rule overrides the target. Inspect computed styles and target the actual background element with a sufficiently specific selector. Inject after the app renders.
Only part of the page changes color Different sections set their own backgrounds. Style the relevant section or app shell as well; do not force every descendant to the same color unless that is intended.
The style works locally but not on another URL The selector is specific to one page layout or the other page renders differently. Use a URL-specific selector or parameterize the selector and color for each page template.
The screenshot is transparent omitBackground: true was used, or the page background itself is transparent. For a solid color, remove omitBackground and set a CSS background on the element that paints the page.
Wait for selector times out The selector is absent, incorrect, or appears after a longer client-side render. Verify the DOM selector and wait for the app’s actual ready state. Increase the timeout only when the page legitimately needs more time.
The PNG has unexpected dimensions fullPage, viewport size, or device scale factor differs from the intended output. Set the viewport explicitly, choose full-page capture intentionally, and account for device scale factor in pixel dimensions.

7. Performance, reliability, and cost

A screenshot requires browser startup, page loading, rendering, and image encoding. Reusing a browser process for multiple captures can avoid repeated startup work, while creating a fresh page per capture keeps page state separate. Always close pages and browsers in cleanup paths, and set navigation and selector timeouts so a slow or stuck site does not hold a worker indefinitely.

For reliable output, control the viewport, wait for a meaningful ready condition, and apply the CSS immediately before capture. Full-page screenshots can use more memory and produce larger files than viewport captures, especially at high device scale factors. Keep the capture dimensions and image format aligned with the output you need.

The local Puppeteer approach has no per-screenshot API fee, but you operate the browser environment and handle its compute, dependencies, and failures. A hosted screenshot API shifts browser operation to a service and may charge according to its own plan and billing rules. Compare those costs against your expected volume and the time spent maintaining browser automation.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. It returns a PNG, JPEG, WebP, or PDF from one request. Its capture flow accepts cookie and consent banners like a visitor, removes 60+ known consent platforms, newsletter popups, and chat widgets, and lets you turn each step off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing; the response includes X-Page-Verdict and X-Billed headers. AI agents can use its MCP server tools: take_screenshot, get_page_info, and capture_pdf.

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 import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));

See the ScreenshotNeo API documentation for setup and options. The API supports full-page captures, CSS selectors, custom CSS and JavaScript, viewport and device presets, waits, request blocking, headers and cookies, caching, async jobs, bulk capture, and more. It does not require you to install or operate a browser for each capture. One thousand screenshots per month are free with no card; paid plans start at $5 for 3,000 screenshots. Create a free account and get 1,000 screenshots a month with no card.

FAQ

Can I set the background color with a screenshot option?

Use CSS in the rendered page. omitBackground enables transparency; it does not accept a solid color.

Does html, body always cover the screenshot?

No. A site may paint its background on an app root, a wrapper, or a separate surface. Target the element that visibly supplies the color.

Can I make only the page margins a custom color?

Yes. Set the color on the appropriate outer page element while leaving content containers’ own backgrounds unchanged.

Where can I check exact option behavior?

Use the Puppeteer documentation matching your installed version, especially the ScreenshotOptions reference and Page.screenshot() API.