ScreenshotNeo

BlogHow-to

How to install BackstopJS and get started with visual testing

Install BackstopJS, capture a visual baseline, run your first comparison, and review changes safely with a repeatable setup.

By the ScreenshotNeo team4 October 20267 min read

Install BackstopJS with npm, initialize it in a project, capture an approved reference, then compare later screenshots against it. The core workflow is backstop init, backstop reference, backstop test, review the report, and run backstop approve only after confirming the changes are intended.

BackstopJS is a visual regression testing tool: it renders configured pages and compares screenshots across runs. It complements functional tests; a visual difference needs review, and a matching screenshot does not prove that an interaction or business rule works. See the BackstopJS project README and its npm package documentation for project and configuration details.

1. Check Node.js and choose an install scope

BackstopJS is distributed through npm. For a quick personal setup, install it globally:

npm install -g backstopjs
backstop --help

For a team project, a local dependency is usually easier to keep reproducible alongside the project and its lockfile:

npm install --save-dev backstopjs

Check that Node.js and npm are available before installing:

node --version
npm --version

When installed locally, invoke the project binary directly or through npm scripts. For example:

./node_modules/.bin/backstop --help

Using a local install avoids relying on each developer or CI worker having the same global package version. The supplied project sources mention Node 20 support in a past release note, but do not establish current runtime support. Check the current package and release information before selecting a Node version for a new project.

2. Initialize BackstopJS safely

From the application or visual-test project root, run:

backstop init

With a local installation, use:

./node_modules/.bin/backstop init

Initialization creates a default backstop.json configuration and supporting files in the current working directory. The README warns that scaffolding can overwrite existing files. Run it in a clean test directory or inspect the destination first. A non-default configuration can be selected later with --config=<path>.

3. Configure a stable first scenario

A scenario identifies a page state to capture. Start with one stable URL, then add important states and viewports as the workflow matures. A scenario needs a descriptive label and a URL; readiness and state controls make captures repeatable.

Example scenario shape for backstop.json:

{
  "id": "my-app",
  "viewports": [
    { "label": "desktop", "width": 1365, "height": 900 }
  ],
  "scenarios": [
    {
      "label": "Home page",
      "url": "http://localhost:3000/",
      "readySelector": "main",
      "readyTimeout": 10000,
      "hideSelectors": [".rotating-promo"],
      "removeSelectors": [],
      "delay": 500
    }
  ],
  "report": ["browser"],
  "engine": "puppeteer"
}

This illustrates common configuration fields; retain or adapt the generated configuration structure for the BackstopJS version installed in your project. Use a readiness selector that appears when the page is genuinely ready. A short delay can allow a known transition to finish, but an arbitrary long delay is slower and can still be unreliable.

Make page state deterministic

  • Use predictable test data and a stable URL. Avoid pages with rotating promotions, random content, live timestamps, or user-specific state unless those are the behaviors under test.
  • Set required cookies or use an onReadyScript to establish a state such as a menu opened by hover or click. Scenario controls include cookies, readyEvent, readySelector, readyTimeout, and scripts.
  • Use hideSelectors when an unstable element should occupy its space but not affect the comparison. Use removeSelectors when it should be removed from the rendered page.
  • Add viewport sizes that reflect the layouts you need to protect. Give each viewport a clear label.
  • Keep scenario labels unique and meaningful so report entries can be traced to the page and state.

4. Capture the reference baseline

Start the app in its expected state, then generate reference screenshots:

backstop reference

For a local installation:

./node_modules/.bin/backstop reference

The reference images are the accepted state against which later test captures are compared. Confirm the application loaded correctly and the intended data, viewport, and UI state are present before treating these images as the baseline.

5. Run a visual test and inspect the report

backstop test

BackstopJS captures the configured scenarios and compares them with their references. Open the generated report and inspect reference, test, and difference views. For each difference, decide whether it is a real regression, an intentional design change, or capture noise caused by timing or dynamic content. A nonzero result is a signal to investigate, not an automatic verdict about product correctness.

6. Approve only reviewed changes

After confirming that the new appearance is expected, update the references:

backstop approve

Approval promotes the preceding test images to the new reference state. Do not approve unexplained changes: doing so can make an unintended regression the new baseline and hide it from future comparisons. In a team, review the changed reference images with the corresponding code change.

Use npm scripts for a repeatable team workflow

Add scripts to package.json so developers and CI use the project-local binary consistently:

{
  "scripts": {
    "visual:init": "backstop init",
    "visual:reference": "backstop reference",
    "visual:test": "backstop test",
    "visual:approve": "backstop approve"
  }
}

Then run, for example:

npm run visual:reference
npm run visual:test

Commit the dependency lockfile and the approved reference assets that your team uses as the comparison baseline. In CI, make sure the application is available at the scenario URLs before running the capture command, and preserve the generated report artifacts when a job fails so the differences can be reviewed.

Configuration options that matter first

Need Configuration approach Use it carefully
Wait for application readiness readySelector, readyEvent, readyTimeout Prefer a signal tied to the page state over a large fixed wait.
Stabilize dynamic regions hideSelectors or removeSelectors Hiding preserves layout space; removing changes layout flow.
Prepare a UI state onReadyScript, cookies, or other scenario setup Make the action repeatable and ensure it runs before capture.
Cover responsive layouts Configure multiple labeled viewport dimensions Start with the breakpoints that matter rather than multiplying scenarios indiscriminately.
Use another config file --config=<path> Keep the chosen config explicit in scripts and CI commands.

BackstopJS also supports browser report output, Chrome Headless rendering, Docker rendering, CLI/JUnit reporting, and interaction scripts using Playwright or Puppeteer, according to its README. Select the execution and report mode that fits your CI environment, and check the installed version’s documentation for exact options.

Troubleshooting common first-run problems

Symptom Likely cause Fix
backstop: command not found BackstopJS is installed locally, or the global npm binary directory is not on PATH. Use ./node_modules/.bin/backstop or an npm script. For a global install, check npm’s global bin path.
Initialization replaces a file backstop init wrote scaffolding into a directory containing an existing file. Restore the file from version control if needed, then initialize in a clean folder or inspect the generated files before running init.
Scenario times out or captures a blank page The app is not running, the URL is wrong, or the configured readiness signal never occurs. Open the URL from the capture environment, verify the app is ready, then correct the URL and readiness selector/event or timeout.
Differences appear on every run Animations, rotating content, timestamps, remote data, or incomplete loading make the page nondeterministic. Stabilize test data and page state; wait for a real readiness condition; hide or remove only genuinely irrelevant regions.
Interaction state is missing The capture starts before the menu, dialog, or authenticated state is established. Set required cookies or prepare the state with an onReadyScript; verify the action selector and timing.
A report shows many expected differences after a redesign The UI changed intentionally, or the viewport/data differs from the reference run. Check environment, viewport, and page state first. Review the images, then approve the intentional update.

Performance, reliability, and maintenance

Capture time grows with the number of scenarios and viewport sizes because each combination needs rendering and comparison. Begin with high-value pages and states, then expand where visual risk justifies the extra runtime. Stable readiness signals improve reliability and are often faster than a blanket delay. Keep the browser environment and test data consistent between baseline creation and CI runs.

BackstopJS’s project README currently includes a notice seeking a maintainer or owner. That is a maintenance caveat, not a statement about whether the package works in a particular project. Before adopting it for a long-lived pipeline, check the latest project releases, package publication details, and supported runtime versions. The research sources do not establish a current latest version or a guaranteed Node compatibility range.

Or skip the browser setup

If the goal is to capture a web page rather than maintain a screenshot comparison suite, ScreenshotNeo provides a website screenshot API and MCP server. It accepts one GET request for a PNG, JPEG, WebP, or PDF capture. Here is the one-call cURL example; see the ScreenshotNeo API documentation for options and response details:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Every feature is on every plan.

Sign up for ScreenshotNeo and get 1,000 free screenshots a month, with no card required.

Frequently asked questions

Does BackstopJS replace unit or end-to-end tests?

No. It detects visual changes in configured captures. Keep behavioral assertions in the test layer designed for them.

Should references be committed?

For a shared workflow, keep the approved references available to developers and CI, commonly under version control or another controlled artifact process. The important point is that test runs compare against a known, reviewed baseline.

Can I test an authenticated page?

Yes. Configure the required cookies or use a setup script to reach the intended state, then make sure the state can be reproduced in the environment that captures screenshots.

Is every visual diff a bug?

No. It may reflect an intended change or nondeterministic content. Review the report before changing the baseline.