ScreenshotNeo

BlogHow-to

How to Capture Full-Page Screenshots with BackstopJS

Capture an entire page with BackstopJS using the document selector, wait for dynamic content, and compare stable visual regression baselines.

By the ScreenshotNeo team4 October 20266 min read

To capture a full page with BackstopJS, set selectors: ["document"] on the scenario. BackstopJS treats document as the entire document; viewport captures only the currently configured viewport. If you omit selectors, the documented default is document, but setting it explicitly makes the test intent clear.

1. Install BackstopJS and create a configuration

Use the BackstopJS documentation for installation requirements and current commands: BackstopJS project documentation. The configuration below is a minimal CommonJS example. Save it as backstop.config.js in your project root and replace the URL with a page your test environment can reach.

module.exports = {
  id: "my-project",
  viewports: [
    { label: "desktop", width: 1280, height: 800 }
  ],
  scenarios: [
    {
      label: "home full page",
      url: "http://localhost:3000/",
      selectors: ["document"]
    }
  ]
};

A configuration needs at least one viewport and one scenario. Each viewport has a label, width, and height; each scenario needs a label and URL. The viewport dimensions set the browser window used for the capture and affect responsive layout. They do not limit the document target to that height.

2. Choose the capture target

Selector What it captures Use it when
document The entire document You want a full-page screenshot or visual regression image.
viewport The current configured viewport You want to compare only the initially visible screen.
A CSS selector The matching element You want to focus a scenario on a component or region.

For full-page capture, use selectors: ["document"]. A CSS element selector is useful for component tests, but it does not mean “full page.” If a selector matches multiple elements, BackstopJS captures the first match by default. Set selectorExpansion: true when you intend to capture every match; with expansion enabled, expect can check the expected number of matches.

3. Wait until the page is ready

Capturing the document does not itself guarantee that client-rendered content, images, or data have finished appearing. Choose a readiness signal that matches the application:

  • readySelector: wait for a known selector to exist in the DOM, such as the main content container.
  • readyEvent: wait for the application to log a chosen message to the console after required content is ready.
  • delay: wait a specified duration before capture. The guide shows a one-second delay as an example, not a universal value.

Example scenario using an application-owned readiness element:

module.exports = {
  id: "my-project",
  viewports: [
    { label: "desktop", width: 1280, height: 800 }
  ],
  scenarios: [
    {
      label: "catalog full page",
      url: "http://localhost:3000/catalog",
      selectors: ["document"],
      readySelector: "[data-test='catalog-ready']"
    }
  ]
};

Use a selector that appears only when the content the screenshot depends on is ready. For readyEvent, your app must emit the configured console message at the right point. A fixed delay is simple, but too short can capture incomplete content and an unnecessarily long delay slows every run. Prefer a real readiness condition when one exists.

4. Make the page state repeatable

Visual regression tests are useful only when the same scenario can be captured consistently. Where practical, use stable test data or fixtures, especially for content that changes frequently. BackstopJS provides page setup hooks for cases that need state preparation:

  • onBeforeScript can set browser state such as cookies before the page is captured.
  • onReadyScript can alter UI state after readiness conditions, for example by clicking or hovering.
  • The documentation also describes importing cookie files through its default onBefore script.

Set only the state needed to reproduce the intended page. Keep authentication, selected tabs, menus, and other interactive states deliberate, so a baseline represents the view you actually want to protect.

Handle changing regions

For content that cannot be stabilized, decide whether it belongs in the comparison. hideSelectors hides selected content while retaining its layout flow. removeSelectors removes elements from the DOM before capture. Hiding or removing changes what the test observes, so use these options only when ignoring that content matches the test’s purpose. Prefer stable fixtures where possible.

5. Create and review reference images

  1. Configure the scenario URL, viewport, capture selector, readiness, and required page state.
  2. Run backstop reference to create reference images.
  3. Run backstop test to capture the current page and generate comparison results.
  4. Inspect the visual report. Run backstop approve only when the changed images represent an intentional update to the accepted baseline.

reference creates baselines; test captures test bitmaps and produces a comparison report; approve promotes changed images from the latest test batch into the reference collection. Keep approval tied to visual review, since approving an unintended change replaces the expected image.

6. Common problems and fixes

Symptom Likely cause What to change
The image shows only the first screen The scenario targets viewport, or a custom selector instead of the document. Set selectors: ["document"] and check that the scenario is using the intended config.
Content is missing from the capture The capture ran before asynchronous rendering or data loading completed. Add an appropriate readySelector or readyEvent; use a measured delay only when necessary.
A readiness wait never completes The selector never appears or the app does not emit the configured event. Verify the selector on that URL and ensure the event is logged after required content is ready.
Repeated runs differ in changing areas Live or randomized content varies between captures. Use stable fixture data where practical, or hide/remove only the regions intentionally excluded from comparison.
Only one of several matching elements is captured BackstopJS captures the first match by default. Enable selectorExpansion: true when all matches should be captured, and set expect to the intended count.
A baseline changed unexpectedly The page or test state changed, or the new image was approved without review. Inspect the report and confirm the intended URL, viewport, readiness, and fixture data before approving.

7. Performance, reliability, and cost considerations

Full-document captures cover more page content than viewport captures, so pages with long documents may take more work to render and review. Keep the configured viewport set focused on screen sizes relevant to your users. Use readiness conditions that reflect actual app state instead of adding a large fixed delay to every scenario. Stable fixtures reduce noisy visual differences and make failures easier to interpret.

BackstopJS’s documented workflow is based on local reference and test images. The research material does not specify runtime benchmarks, hosted service pricing, or resource requirements, so plan capacity by running your own representative scenarios. Keep reference updates under review; a consistent capture process and intentional approvals are central to reliable visual comparisons.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF, and its API accepts common screenshot parameter names to make switching easier. See the ScreenshotNeo API documentation for options.

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}`);

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are never billed, and responses identify the page verdict and billing status in headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for AI agents. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for 1,000 free screenshots a month, with no card.

FAQ

Does BackstopJS default to a full-page capture?

Yes. The documented default for selectors is document. Setting it explicitly is useful when you want the full-page intent to be obvious in the scenario.

Does the viewport height limit a document capture?

No. The viewport defines the browser screen size; document is the full-document target. Use viewport when you want only the visible viewport.

Should I approve every changed screenshot?

No. Review the comparison and approve only changes that are intended to become the new reference.