Skip to content

Migrate from BackstopJS ​

BackstopJS is a visual-regression tool — still widely used, but drifting — that captures URL/element screenshots with Puppeteer (or Playwright), diffs them with Resemble.js, and shows an interactive HTML report; this guide migrates its config to Dungbeetle.

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 discovers recent runs (dungbeetle_list_runs), reads semantic diffs, and acts under a scoped, revocable token; promoting a baseline stays 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 \
  --client-id "$DUNGBEETLE_CLIENT_ID" --client-secret "$DUNGBEETLE_CLIENT_SECRET"

Pushes go to the hosted cloud by default. Self-hosting? Point the CLI at your server with --server <url> or DUNGBEETLE_SERVER_URL.

Next ​

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