Percy Screenshots Are Blank: How to Troubleshoot Them
Find where a blank Percy screenshot went wrong: page readiness, DOM capture, asset discovery, upload, or remote rendering.
A blank Percy screenshot usually means the page was captured before it was ready, required assets were unavailable to Percy, or the remote rendering stage handled the captured DOM differently than expected. First check whether the target UI was visible in the test browser at the instant percySnapshot ran. Then inspect the captured DOM, discovered assets, Percy’s Network logs, and the build diagnostics to identify the failing stage.
Percy does not simply upload the test browser’s screenshot. Its SDK captures the DOM and discovers its assets in the test browser; Percy then renders that snapshot in its infrastructure across browser and viewport configurations. A page that looks correct locally can therefore still render blank if the snapshot was early, an asset could not be fetched, or remote rendering behaved differently. See BrowserStack’s Percy troubleshooting guide and Percy documentation.
1. Separate a blank snapshot from a missing snapshot
These are different failures and need different checks:
- A snapshot exists but renders blank or incomplete: investigate page readiness, serialized DOM, asset discovery, JavaScript configuration, and remote rendering.
- The Percy build has no snapshots: check that the test invoked the Percy SDK or CLI command, that CI had a valid
PERCY_TOKEN, and that a parallel build was finalized after all shards completed.
Before changing settings, record six facts: whether the UI was visible in the test browser; whether the intended DOM was captured; whether its assets were discovered and fetched; whether the issue affects all browsers and widths; whether snapshots uploaded and the build finalized; and whether the problem reproduces with JavaScript disabled and a stable wait.
2. Confirm the test browser showed the page
- Pause immediately before the Percy snapshot call.
- Inspect the current page in the test browser, or capture a local browser screenshot and inspect the relevant DOM.
- Confirm the target component has real content, not a spinner, skeleton, empty state, or placeholder.
If the UI is absent in the test browser too, Percy cannot fix the application state or test flow. Correct the navigation, authentication, test data, or asynchronous application behavior first. If the UI is present in the browser but absent from Percy’s result, continue with capture timing, DOM serialization, discovered assets, and remote rendering.
3. Wait for the application’s meaningful ready state
Navigation completion does not always mean an application has fetched data, hydrated, or rendered the component you want. Take the snapshot after a meaningful selector or application state becomes ready. Prefer a stable element or explicit app signal over a guessed delay.
Example: wait for a selector in a browser test
// Playwright example; adapt the selector and test setup to your app.
await page.goto('https://your-app.example/dashboard');
await page.locator('[data-testid="dashboard-ready"]').waitFor({ state: 'visible' });
await percySnapshot(page, 'Dashboard ready');
If there is no stable selector, wait for the application’s own readiness signal. A fixed delay can be a temporary diagnostic, but it can be too short on a slow CI worker and waste time on a fast one. BrowserStack’s guidance for script-based CLI snapshots also describes waiting for a selector or a timeout; Percy’s Cypress guidance recommends snapshotting after meaningful states such as navigation, interaction, or completed asynchronous loading.
Example: Cypress snapshot after visible content
// Ensure the Percy Cypress SDK is installed and configured in the test project.
cy.visit('/dashboard');
cy.get('[data-testid="dashboard-ready"]').should('be.visible');
cy.percySnapshot('Dashboard ready');
Use the snapshot function and wait APIs appropriate to the framework and Percy SDK version in your project. The key condition is that the target state is present before the snapshot call.
4. Inspect the DOM Percy captured
When the browser showed the right page but Percy did not, determine whether the captured DOM contains the target content. Check for:
- Conditional rendering that removes the component before the snapshot.
- State changes triggered by timers, route transitions, or asynchronous requests that have not completed.
- Content inside an iframe, shadow root, or browser-only feature that may not be represented as expected in the captured snapshot.
- Responsive markup that differs at the Percy viewport from the test browser viewport.
- Scripts that mutate the DOM after capture or rely on being rerun by the remote renderer.
By default, Percy renders captured snapshots with JavaScript disabled because the application’s JavaScript has already run and modified the DOM in the test browser. If enable-javascript: true is set and the result is blank or times out, test with JavaScript disabled. Enabling JavaScript is appropriate only when the rendered snapshot requires it; scripts may run again against an already formed DOM and produce unexpected behavior.
5. Find missing or inaccessible assets
A page can have the right DOM yet look blank or incomplete because its styles, images, fonts, or other resources did not reach Percy’s renderer. In the Percy build, inspect Network logs and asset-discovery output. Look for failed requests, unusually slow resources, blocked private hosts, authentication failures, and CI timeouts.
- Confirm private asset hosts are permitted and reachable from Percy’s rendering infrastructure.
- Check whether assets require credentials or headers that are present in the test browser but unavailable to the remote renderer.
- For lazy-loaded images or sections, scroll far enough before capture to trigger loading, then wait for the resulting content.
- For responsive images, review Percy’s documented
captureSrcsetoption and confirm the expected source is captured. - Inspect CSS and font requests as well as image requests; missing stylesheets can make otherwise present content appear empty or unstyled.
6. Use Percy diagnostics to locate the failing stage
- Open the build’s smart-debug Overview and follow the classified failure and relevant log line.
- Open Network logs to identify missing assets or slow third-party resources.
- For local asset investigation, run
npx percy exec --debug -- [test command]. This performs SDK capture and asset discovery without creating a build or uploading snapshots. - When uploading snapshots, use
--verbosefor detailed logs. Compare whether the failure is in capture, asset discovery, upload, or remote rendering before changing multiple settings.
If the log says snapshots failed or could not upload, investigate network stability and upload logs separately from a snapshot that uploaded but rendered blank.
7. Check CI wiring, tokens, and parallel builds
For a build with no snapshots, verify the snapshot command ran in the job that should create them, the Percy SDK or CLI is wired into that command, and the CI environment contains the correct PERCY_TOKEN. In parallel builds, confirm the build is finalized only after every shard completes. A missing token or skipped command is not a blank-rendering problem.
If only some snapshots are missing, inspect pending CI requests and concurrency. The official troubleshooting guidance suggests reducing concurrency to 1 as a diagnostic. If doing so changes the result, investigate timing, worker load, and build finalization rather than treating it as proof of a rendering defect.
8. Narrow down viewport-specific and intermittent failures
Record whether the issue repeats across runs, browsers, and widths. If it fails at one width, inspect responsive markup, viewport-dependent requests, and per-browser network and rendering details. Percy documents deferred upload as a way to load DOMs on a specific device; use the relevant documentation when the DOM itself must be loaded at the target device configuration.
Intermittent blanks often point to readiness races, slow third-party resources, or unstable CI requests. Re-run with an explicit ready selector and compare the DOM and asset evidence. Change one variable at a time so the result identifies a cause.
Common symptoms and first fixes
| Symptom | Likely cause | What to check or change |
|---|---|---|
| Entire screenshot is blank or stuck loading | Snapshot taken before app readiness; JavaScript reran remotely | Wait for a meaningful selector or app state; if JavaScript was enabled, test the default disabled setting. |
| One element is missing | Element was not rendered at capture time, or its assets were unavailable | Check the DOM at snapshot time, lazy loading, responsive behavior, and asset discovery. |
| Images, fonts, or styling are missing | Host access, auth, network timing, lazy loading, or responsive source issue | Review Network logs, allowed hosts, credentials, timeouts, and captureSrcset. |
| Build contains no snapshots | SDK or CLI command did not run, token missing, or parallel build not finalized | Verify command execution, PERCY_TOKEN, shard completion, and finalization. |
| Only some snapshots are missing | Pending requests, concurrency, or intermittent CI behavior | Review CI logs and try concurrency 1 to test whether timing is involved. |
| Upload failed | Network or upload problem, distinct from rendering | Inspect verbose upload logs and network stability; establish whether a snapshot reached Percy. |
| Browser looks right but Percy does not | Captured DOM, asset discovery, JavaScript, or responsive render differs | Compare the test browser state, captured DOM, fetched assets, and per-browser render details. |
Performance, reliability, and cost considerations
Waiting for a specific ready condition makes runs more reliable than choosing a delay that only works on a fast worker. Keep the condition narrow and tied to the content being tested; waiting for every network request can be brittle when analytics or long polling never stop. For slow assets, identify the request in logs before increasing timeouts. Lower concurrency is useful to isolate a scheduling or load issue, but it can increase total build time and should be treated as a diagnostic until the cause is clear.
Run Percy’s local debug mode when investigating discovery because it avoids creating a build or uploading snapshots. Use full verbose upload logs only when the failure may occur during upload. This keeps diagnosis focused and avoids spending time or build capacity on repeated blind retries. The research dossier does not establish Percy pricing or a measured blank-screenshot rate, so this guide makes no numerical claim about either.
Or skip the browser setup
For a one-off screenshot outside the Percy visual-test workflow, ScreenshotNeo can capture a URL with one API request. It accepts a URL and returns PNG, JPEG, WebP, or PDF. Its clean-shot flow accepts consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and each response identifies the page verdict and billing status in headers. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture.
cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
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)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
const bytes = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));
See the ScreenshotNeo API docs for request options. The service also offers an MCP server for AI agents, bulk capture for up to 100 URLs per call, signed links for public image tags, asynchronous jobs with signed webhooks, a usage API, and custom capture controls. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Every feature is available on every plan.
Sign up free for 1,000 screenshots a month, with no card required.
FAQ
Does a Percy blank screenshot mean the browser test failed?
Not necessarily. The test browser may show the page while Percy’s captured DOM, discovered assets, upload, or remote rendering produces a different result.
Should JavaScript be enabled for Percy rendering?
Usually the default is disabled because the SDK captures the DOM after the application’s JavaScript has run. Enable it only when the snapshot requires remote script execution, and investigate rerun side effects.
What is the fastest first diagnostic?
Check the test browser at snapshot time, wait for the target selector, then inspect Percy’s Overview and Network logs. These checks distinguish readiness from asset and rendering problems.
Can ScreenshotNeo replace Percy?
ScreenshotNeo is a website screenshot API and MCP server for capturing URLs. Percy’s workflow captures DOM snapshots for visual testing across browsers and widths, so choose the tool that matches whether you need a direct URL capture or an integrated visual regression workflow.


