Percy readiness gate for page load
Prevent snapshots from capturing loading spinners, skeleton screens, or missing images by configuring Percy to wait for your page to finish loading.
Percy captures your page’s DOM at a specific point in time and compares the rendered snapshot with the baseline. Pages often load content in stages. APIs return data, images and web fonts download, and JavaScript updates the layout after the page first appears. If the capture happens before this finishes, the snapshot contains incomplete content, and you see visual diffs that don’t result from your code changes.
Use this guidance if your Percy builds show any of the following:
- Loading spinners, skeleton screens, or placeholder content in snapshots.
- Missing images or text rendered in a fallback font.
- Partially rendered charts, tables, or other dynamic content.
- Different diffs across builds, even when the page hasn’t changed.
- A recommendation on the Percy build page to adjust readiness settings.
To prevent these issues, Percy provides a feature called the readiness gate. The readiness gate makes Percy wait until your page stops changing before it captures a snapshot.
The readiness gate requires Percy CLI v1.32.5 or later. We recommend that you use the latest version.
How the readiness gate works
Before each snapshot, Percy waits for the following conditions:
- The page layout stops changing.
- Network activity goes quiet.
- Web fonts and visible images finish loading.
- Page scripts go idle.
By default, Percy waits up to 10 seconds. If the page isn’t ready by then, Percy captures it as is and the build continues. To change this limit, choose a different preset or set timeout-ms.
Some Percy SDKs run the readiness gate by default. Others, and the percy snapshot command, run it only after you configure it. To make sure the readiness gate runs, add a readiness block to your .percy.yml file.
Configure the readiness gate
You configure the readiness gate in the readiness block of your .percy.yml file. Settings in this file apply to every snapshot. To use different settings for specific snapshots, see Override settings for a single snapshot.
Start by choosing a preset. If snapshots still capture incomplete content, tell Percy which elements to wait for. Customize a preset only when none of them fits your page.
Choose a preset
A preset is a ready-made combination of readiness settings. It controls how long the page must stay unchanged and how long Percy waits in total.
The following table describes each preset:
| Preset | Page must stay unchanged for | Maximum wait | Use when |
|---|---|---|---|
balanced |
300 ms | 10 seconds | Most pages. This is the default preset. |
strict |
1000 ms | 30 seconds | Pages with long animations, charts, or content that loads in several stages. |
fast |
100 ms | 5 seconds | Static or lightweight pages where build speed matters more than waiting. This preset doesn’t wait for images. |
disabled |
N/A | N/A | Pages where your test code already waits for loading to finish. |
Percy uses the balanced preset by default. To choose a different preset, set preset in your .percy.yml file:
To turn off the readiness gate, set preset to disabled. Use this when your test code already waits for the page to load.
Wait for specific elements
A preset waits for the page as a whole to settle. Some pages look settled while they still show a loading state, such as a spinner that stays on screen while data loads. For these pages, tell Percy which elements show that loading has finished:
-
ready-selectors: Percy waits until every listed element appears on the page and is visible. Use it for an element that appears only after loading finishes. An element hidden withdisplay: none, or inside a hidden parent, doesn’t count as visible. -
not-present-selectors: Percy waits until none of the listed elements exist on the page. Use it for spinners, skeleton screens, and loading overlays. Percy checks whether the element exists, not whether it’s visible. If your app hides a spinner withdisplay: nonebut leaves it on the page, Percy waits for the full timeout on every snapshot. Use a selector that matches only the visible state, such as.spinner.is-active.
Both options accept CSS selectors and XPath expressions, and you can mix both in one list. Percy treats a selector as XPath only when it starts with /, //, ./, (/, or (./. Percy treats any other selector as CSS. For example, id('root') runs as CSS and never matches. To set the type explicitly, use an object such as css: "#header" or xpath: "//main" as the list item.
Customize a preset
If no preset fits your page, change the individual values that a preset sets, such as the maximum wait time. Any value you set replaces the matching value from your preset, and the preset supplies the rest. To skip one condition, set it to false. For example, set dom-stability: false for pages with live data or continuous animations, where the layout never stops changing.
Use kebab-case keys, such as timeout-ms, in .percy.yml. Use camelCase keys, such as timeoutMs, in your test code in every language, including Python, Ruby, and .NET. In Selenium SDKs, use timeoutMs so that the SDK raises the driver’s script timeout to match. The following table lists all settings:
.percy.yml key |
Test code option | Description |
|---|---|---|
preset |
preset |
The preset to start from: balanced, strict, fast, or disabled. Default: balanced. |
timeout-ms |
timeoutMs |
The maximum time, in milliseconds, that Percy waits before it captures the snapshot anyway. Range: 1000 to 60000. |
max-timeout-ms |
maxTimeoutMs |
An upper limit on timeout-ms. Set it to keep the wait under your test driver’s script timeout. Range: 1000 to 60000. |
stability-window-ms |
stabilityWindowMs |
How long, in milliseconds, the page layout must stay unchanged. Range: 50 to 30000. |
network-idle-window-ms |
networkIdleWindowMs |
How long, in milliseconds, the page must go without a new network request. Range: 50 to 10000. |
js-idle-window-ms |
jsIdleWindowMs |
How long, in milliseconds, page scripts must stay idle. Range: 50 to 30000. |
dom-stability |
domStability |
Set to false to stop waiting for the layout to stop changing. |
image-ready |
imageReady |
Set to false to stop waiting for visible images. |
font-ready |
fontReady |
Set to false to stop waiting for web fonts. |
js-idle |
jsIdle |
Set to false to stop waiting for page scripts to go idle. |
ready-selectors |
readySelectors |
A list of CSS or XPath selectors. Percy waits until every matching element appears and is visible. |
not-present-selectors |
notPresentSelectors |
A list of CSS or XPath selectors. Percy waits until no matching element exists. |
Override settings for a single snapshot
Settings in .percy.yml apply to every snapshot in your build. Some pages need different settings, such as a dashboard that loads more slowly than the rest of your app.
For these pages, pass a readiness object in the snapshot options in your test code. You can use any setting from the previous sections. These settings apply to that snapshot only:
// Playwright
await percySnapshot(page, 'Dashboard', {
readiness: {
preset: 'strict',
readySelectors: ['#chart[data-rendered="true"]'],
notPresentSelectors: ['.loading-overlay']
}
});
Percy combines the two configurations one setting at a time. Each setting you pass for a snapshot replaces the same setting from .percy.yml, and all other settings come from .percy.yml. This has the following effects:
- A selector list you pass for a snapshot replaces the list in
.percy.yml. It doesn’t add to it. To keep the selectors from.percy.yml, include them in the snapshot’s list too. - If
.percy.ymlsetspresettodisabled, the readiness gate stays off for every snapshot, even one where you change other settings. To turn the gate on for one snapshot, setpresetin that snapshot’s options.
Troubleshoot readiness issues
To see readiness logs, including which conditions timed out, run Percy with the --verbose flag. The following table lists common issues and fixes:
| Issue | Fix |
|---|---|
| Snapshots still show a loading state | Add the loading element, such as .skeleton or .spinner, to not-present-selectors. Or add an element that appears only after loading to ready-selectors. |
| Snapshots take too long | Switch to the fast preset, or lower timeout-ms. If a page never settles, Percy waits for the full timeout on every snapshot. |
| Readiness times out on pages that update constantly | Set dom-stability: false for pages with live data, tickers, or continuous animations. The other conditions still apply. |
Every snapshot waits for the full timeout when you use not-present-selectors
|
Your app hides the loading element but leaves it on the page. Percy checks whether the element exists, not whether it’s visible. Use a selector that matches only the visible state, such as .spinner.is-active. |
| A selector never matches | Check that XPath selectors start with /, //, ./, (/, or (./. Percy runs any other selector as CSS. |
A selector in ready-selectors never passes |
Check that the element is visible. Elements hidden with display: none or inside a hidden parent don’t pass. |
| Tests hang when the page uses fake timers | Set preset to disabled for tests that pause or fake timers, such as Playwright page.clock or Jest and Sinon fake timers. |
| Selenium script timeout errors | Set timeoutMs in your snapshot options, not timeout_ms. With timeoutMs, the SDK raises the driver’s script timeout to match. Or set max-timeout-ms below your driver’s script timeout. |
For other snapshot issues, see Troubleshoot common issues.
We're sorry to hear that. Please share your feedback so we can do better
Contact our Support team for immediate help while we work on improving our docs.
We're continuously improving our docs. We'd love to know what you liked
We're sorry to hear that. Please share your feedback so we can do better
Contact our Support team for immediate help while we work on improving our docs.
We're continuously improving our docs. We'd love to know what you liked
Thank you for your valuable feedback!