Skip to content

Migrate from BackstopJS

BackstopJS captures URL/element screenshots with Puppeteer (or Playwright), diffs them with Resemble.js, and shows an interactive HTML report. It's still widely used — but drifting.

BackstopJS maintenance is stalling

There's been no new npm release in roughly a year and Snyk classifies the package's maintenance as inactive. It isn't archived, but dependency and security drift is the trajectory — and it has no central, hosted review UI (baselines are git-committed PNGs, approvals are a local CLI). A good time to move to a maintained tool with real review.

Why Dungbeetle

BackstopJSDungbeetle
MaintenanceInactive (no release ~12 mo)Active
DiffPixel (Resemble.js)Structured tree (+ tolerant pixel fallback)
EnginePuppeteer / PlaywrightPlaywright/Chromium
Approve flowbackstop approve (local CLI)local re-baseline or hosted promote in the review UI
Central reviewNo (local HTML report)Yes — self-host or managed
Agent accessNoneMCP server — an agent can triage runs, but promoting a baseline is human-owned approval
Cross-machine flakinessHigh without the Docker imageLower — structured diffs don't depend on render pixels

Translate your config

BackstopJS uses backstop.json. scenarios × viewports become web targets:

json
// backstop.json
{
  "id": "my_project",
  "viewports": [
    { "label": "phone",   "width": 320,  "height": 480 },
    { "label": "desktop", "width": 1920, "height": 1080 }
  ],
  "scenarios": [
    {
      "label": "Homepage",
      "url": "https://example.com",
      "selectors": ["document"],
      "delay": 500,
      "misMatchThreshold": 0.1
    }
  ],
  "engine": "puppeteer"
}
json
// dungbeetle.config.json
{
  "version": 1,
  "project": { "name": "my_project" },
  "lifecycle": {
    "capture": [
      { "kind": "web", "name": "Homepage-phone",   "driver": "playwright", "url": "https://example.com", "screenshot": true, "viewport": { "width": 320,  "height": 480 } },
      { "kind": "web", "name": "Homepage-desktop", "driver": "playwright", "url": "https://example.com", "screenshot": true, "viewport": { "width": 1920, "height": 1080 } }
    ]
  },
  "comparison": { "pixelTolerance": { "maxChangedRatio": 0.001 } }
}

Field mapping:

BackstopJSDungbeetle
scenarios[].urlcapture[].url
scenarios[].label × viewports[].labelcapture[].name (one target per scenario × viewport)
viewports[]capture[].viewport
misMatchThreshold (%)comparison.pixelTolerance.maxChangedRatio (ratio: 0.1%0.001)
engine (puppeteer/playwright)driver: "playwright" + browser.channel
delay / readySelectorlifecycle.wait (e.g. wait on a URL/selector before capture)
selectors / hideSelectorssee below
paths.bitmaps_referencedungbeetle.snapshots/ (baselines dir)

hideSelectors → masking, or just use the DOM diff

BackstopJS hides dynamic regions (hideSelectors) to stop pixel flakiness. In Dungbeetle, two better options: (1) capture the structured DOM (drop screenshot) so a single changed node reads as one node change instead of a repaint; (2) for dynamic text (timestamps, IDs), add a normalization mask that replaces the value everywhere before comparison. You usually need far fewer "ignore" rules than with pixels.

Map the workflow

BackstopJSDungbeetle
backstop referencedungbeetle update
backstop testdungbeetle test (or dungbeetle ci for JSON/HTML)
backstop approvere-run dungbeetle update, or promote in the cloud review UI
sh
dungbeetle update                                   # ≈ backstop reference
dungbeetle test                                     # ≈ backstop test
dungbeetle ci --json report.json --html report.html # in CI (replaces JUnit + HTML report)

Commit dungbeetle.snapshots/. Your backstop_data/bitmaps_reference PNGs don't carry over — re-baseline once.

Add central review (what BackstopJS lacks)

BackstopJS approvals are local. Push to a Dungbeetle cloud server for a shared, audited review → approve → promote flow across machines and repos:

sh
dungbeetle push --report report.json \
  --server "$DUNGBEETLE_SERVER_URL" \
  --client-id "$DUNGBEETLE_CLIENT_ID" --client-secret "$DUNGBEETLE_CLIENT_SECRET"

Next

Source-available: CLI under FSL-1.1-ALv2, cloud server under BUSL-1.1. See Licensing.