{"id":59809,"date":"2026-03-19T20:32:00","date_gmt":"2026-03-20T00:32:00","guid":{"rendered":"https:\/\/codesamplez.com\/?p=59809"},"modified":"2026-03-19T20:32:04","modified_gmt":"2026-03-20T00:32:04","slug":"visual-regression-testing-github-actions","status":"publish","type":"post","link":"https:\/\/codesamplez.com\/devops\/visual-regression-testing-github-actions","title":{"rendered":"SnapDrift: Free Auto Visual Regression Testing On GitHub Actions"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">If your CI pipeline doesn&#8217;t catch visual regressions, you&#8217;re shipping UI bugs with your eyes closed. I&#8217;ve lost count of how many times a &#8220;safe&#8221; CSS refactor nuked a hero section on mobile, and nobody noticed until a customer screenshot landed in Slack. Unit tests don&#8217;t catch layout drift. Integration tests don&#8217;t catch it either. You need pixel-level proof that what shipped <em>still looks right<\/em>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s exactly why I built <strong>SnapDrift<\/strong> \u2014 an free and open-source visual regression tool that captures full-page screenshots, compares them against a known baseline, and reports drift directly inside your GitHub pull requests. No external services, no SaaS dashboards, no vendor lock-in. Just two GitHub Actions and a JSON config.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this post, I&#8217;ll walk you through what SnapDrift does, why it exists, and how to integrate it into your project in under 10 minutes.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-the-problem-ux-changes-are-invisible-until-they-re-not\">The Problem: UX Changes Are Invisible Until They&#8217;re Not<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s the scenario every frontend developer knows too well. You update a shared utility class, maybe tweak some padding or swap a font weight. Your unit tests pass. Your integration tests pass. You merge the PR with confidence. Then three days later someone notices the checkout button is overlapping the footer on mobile.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Visual bugs are uniquely painful because they&#8217;re almost impossible to catch in code review. A reviewer staring at a diff of <code>padding: 12px \u2192 16px<\/code> has no way of knowing that change causes a layout break on a different page. What you need is automated, pixel-level diffing \u2014 and that&#8217;s where SnapDrift comes in.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-what-is-snapdrift\">What Is SnapDrift?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>SnapDrift<\/strong> is a set of composable GitHub Actions that handle visual regression testing inside your existing CI workflows. You own the checkout, build, and startup. SnapDrift takes over once your app is reachable.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s what it handles end to end:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Baseline capture on <code>main<\/code><\/strong> \u2014 every push to your default branch saves a fresh set of full-page screenshots as a GitHub artifact.<\/li>\n\n\n\n<li><strong>Pull request drift detection<\/strong> \u2014 on every PR, SnapDrift captures the same routes, downloads the latest baseline, and runs a pixel-level comparison.<\/li>\n\n\n\n<li><strong>Route scoping from changed files<\/strong> \u2014 if you&#8217;ve configured file-to-route mappings, SnapDrift only captures the routes affected by the PR&#8217;s changeset. Fewer screenshots, faster runs.<\/li>\n\n\n\n<li><strong>PR report upserts<\/strong> \u2014 SnapDrift posts (and updates) a comment on the PR with comparison results, including which routes drifted and by how much.<\/li>\n\n\n\n<li><strong>Drift enforcement<\/strong> \u2014 configurable modes from <code>report-only<\/code> (just inform) to <code>strict<\/code> (fail the build on any visual change).<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The entire thing runs on GitHub-hosted Ubuntu runners using Playwright Chromium under the hood. No Docker images to manage, no external screenshot services.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">\ud83d\udca1 <strong>Pro Tip:<\/strong> Start with <code>report-only<\/code> mode while your baselines stabilize. Once you trust the signal, switch to <code>fail-on-changes<\/code> or <code>strict<\/code> to make visual regressions a hard gate.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-quickstart-full-integration-in-3-steps\">Quickstart: Full Integration in 3 Steps<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The setup is intentionally minimal. One config file, two workflow steps.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-step-1-add-the-config-file\">Step 1 \u2014 Add the Config File<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Create <code>.github\/snapdrift.json<\/code> in your repository root:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>{\n  \"baselineArtifactName\": \"my-app-snapdrift-baseline\",\n  \"workingDirectory\": \".\",\n  \"baseUrl\": \"http:\/\/127.0.0.1:8080\",\n  \"resultsFile\": \"qa-artifacts\/snapdrift\/baseline\/current\/results.json\",\n  \"manifestFile\": \"qa-artifacts\/snapdrift\/baseline\/current\/manifest.json\",\n  \"screenshotsRoot\": \"qa-artifacts\/snapdrift\/baseline\/current\",\n  \"routes\": &#91;\n    { \"id\": \"home-desktop\", \"path\": \"\/\", \"viewport\": \"desktop\" },\n    { \"id\": \"home-mobile\", \"path\": \"\/\", \"viewport\": \"mobile\" }\n  &#93;,\n  \"diff\": { \"threshold\": 0.01, \"mode\": \"report-only\" }\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4.5 12.75l6 6 9-13.5\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #8FBCBB\">baselineArtifactName<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">my-app-snapdrift-baseline<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #8FBCBB\">workingDirectory<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">.<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #8FBCBB\">baseUrl<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">http:\/\/127.0.0.1:8080<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #8FBCBB\">resultsFile<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">qa-artifacts\/snapdrift\/baseline\/current\/results.json<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #8FBCBB\">manifestFile<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">qa-artifacts\/snapdrift\/baseline\/current\/manifest.json<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #8FBCBB\">screenshotsRoot<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">qa-artifacts\/snapdrift\/baseline\/current<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #8FBCBB\">routes<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&#91;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">{<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #8FBCBB\">id<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">home-desktop<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #8FBCBB\">path<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">\/<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #8FBCBB\">viewport<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">desktop<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">},<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">{<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #8FBCBB\">id<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">home-mobile<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #8FBCBB\">path<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">\/<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #8FBCBB\">viewport<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">mobile<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #ECEFF4\">&#93;,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #8FBCBB\">diff<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #8FBCBB\">threshold<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.01<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #8FBCBB\">mode<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">report-only<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">}<\/span><\/span><\/code><\/pre><span style=\"display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-end;background-color:#2e3440ff;color:#c8d0e0;font-size:12px;line-height:1;position:relative\">JSON<\/span><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">A few things to note here. The <code>baseUrl<\/code> is wherever your app starts during CI \u2014 if you&#8217;re using Next.js on port 3000, change it to <code>http:\/\/127.0.0.1:3000<\/code>. The <code>routes<\/code> array defines what SnapDrift captures: each route gets an <code>id<\/code>, a URL <code>path<\/code>, and a <code>viewport<\/code> preset (<code>desktop<\/code> at 1440\u00d7900 or <code>mobile<\/code> at 390\u00d7844). And <code>diff.threshold<\/code> controls sensitivity \u2014 <code>0.01<\/code> means even 1% pixel drift gets flagged.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-step-2-capture-baselines-on-push-to-main\">Step 2 \u2014 Capture Baselines on Push to <code>main<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In your main branch CI workflow (e.g., <code>.github\/workflows\/ci.yml<\/code>), add the baseline step <em>after<\/em> your app is built and running:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>- name: SnapDrift Baseline\n  uses: ranacseruet\/snapdrift\/actions\/baseline@v0.1.0\n  with:\n    repo-config-path: .github\/snapdrift.json<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4.5 12.75l6 6 9-13.5\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #ECEFF4\">-<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">name<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">SnapDrift Baseline<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #8FBCBB\">uses<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">ranacseruet\/snapdrift\/actions\/baseline@v0.1.0<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #8FBCBB\">with<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #8FBCBB\">repo-config-path<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">.github\/snapdrift.json<\/span><\/span><\/code><\/pre><span style=\"display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-end;background-color:#2e3440ff;color:#c8d0e0;font-size:12px;line-height:1;position:relative\">YAML<\/span><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">This captures screenshots of every configured route and uploads them as a GitHub artifact. Every subsequent PR will be compared against this baseline.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-step-3-run-drift-detection-on-pull-requests\">Step 3 \u2014 Run Drift Detection on Pull Requests<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In your PR workflow, add the diff step:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>- name: SnapDrift Report\n  uses: ranacseruet\/snapdrift\/actions\/pr-diff@v0.1.0\n  with:\n    github-token: ${{ secrets.GITHUB_TOKEN }}\n    repo-config-path: .github\/snapdrift.json<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4.5 12.75l6 6 9-13.5\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #ECEFF4\">-<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">name<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">SnapDrift Report<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #8FBCBB\">uses<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">ranacseruet\/snapdrift\/actions\/pr-diff@v0.1.0<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #8FBCBB\">with<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #8FBCBB\">github-token<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">${{ secrets.GITHUB_TOKEN }}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #8FBCBB\">repo-config-path<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">.github\/snapdrift.json<\/span><\/span><\/code><\/pre><span style=\"display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-end;background-color:#2e3440ff;color:#c8d0e0;font-size:12px;line-height:1;position:relative\">YAML<\/span><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s the entire integration. SnapDrift downloads the latest baseline artifact, captures the PR&#8217;s state, runs the pixel comparison, and posts a comment on the PR with the results. If you&#8217;re using <code>fail-on-changes<\/code> or <code>strict<\/code> mode, it&#8217;ll also fail the workflow run when drift is detected.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">\ud83d\udca1 <strong>Pro Tip:<\/strong> Security-conscious teams can pin the action to a specific commit SHA instead of the tag. Resolve <code>v0.1.0<\/code> to its SHA and use that directly \u2014 <code>uses: ranacseruet\/snapdrift\/actions\/baseline@&lt;commit-sha&gt;<\/code>.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-understanding-drift-modes\">Understanding Drift Modes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">SnapDrift ships with four enforcement levels. Pick the one that matches your team&#8217;s tolerance for visual changes:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Mode<\/th><th>Fails the run when\u2026<\/th><\/tr><\/thead><tbody><tr><td><code>report-only<\/code><\/td><td>Never \u2014 just posts the comparison report<\/td><\/tr><tr><td><code>fail-on-changes<\/code><\/td><td>Any capture exceeds the pixel threshold<\/td><\/tr><tr><td><code>fail-on-incomplete<\/code><\/td><td>Captures are missing, dimensions shifted, or comparison errored<\/td><\/tr><tr><td><code>strict<\/code><\/td><td>Any drift signal <em>or<\/em> incomplete comparison<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">My recommendation: start with <code>report-only<\/code> for the first week or two. Let baselines accumulate, make sure the routes are stable, and review a few PR reports to calibrate the threshold. Then move to <code>fail-on-changes<\/code> once you trust the signal. <code>strict<\/code> is for teams that want zero ambiguity \u2014 any anomaly at all blocks the merge.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-what-snapdrift-doesn-t-do-on-purpose\">What SnapDrift Doesn&#8217;t Do (On Purpose)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I want to be upfront about current constraints. SnapDrift is deliberately scoped:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Ubuntu runners only<\/strong> \u2014 Playwright Chromium needs Ubuntu&#8217;s system dependencies. macOS and Windows runners aren&#8217;t supported yet.<\/li>\n\n\n\n<li><strong>Full-page capture only<\/strong> \u2014 no element-level or component-level screenshot targeting. You&#8217;re comparing entire pages.<\/li>\n\n\n\n<li><strong>Fixed viewport presets<\/strong> \u2014 <code>desktop<\/code> (1440\u00d7900) and <code>mobile<\/code> (390\u00d7844). Custom viewports aren&#8217;t configurable yet.<\/li>\n\n\n\n<li><strong>One global threshold<\/strong> \u2014 the <code>diff.threshold<\/code> in your config applies to all routes. Per-route thresholds are on the roadmap.<\/li>\n\n\n\n<li><strong>Dimension shifts are separate<\/strong> \u2014 if a page&#8217;s height changes (say, a new section was added), that&#8217;s flagged differently from pixel-level color drift.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">These aren&#8217;t accidents \u2014 they&#8217;re deliberate scope cuts for v0.1.0. The goal was to ship something that solves the 80% case reliably rather than trying to cover every edge case on day one.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-why-i-built-this\">Why I Built This<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">There are paid tools in this space \u2014 Percy, Chromatic, Applitools. They&#8217;re good products. But they all share the same problem: they&#8217;re external services with per-screenshot pricing that adds up fast, especially on active repos with lots of routes. And for open-source projects or small teams, that pricing model just doesn&#8217;t work.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I wanted something that runs entirely inside GitHub Actions, stores artifacts using GitHub&#8217;s own infrastructure, and costs nothing beyond your existing CI minutes. SnapDrift is that tool. It&#8217;s MIT-licensed, open source, and designed to be composed into whatever CI workflow you already have.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-what-s-next\">What&#8217;s Next<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">SnapDrift is at v0.1.0 \u2014 it&#8217;s functional and ready for production use, but there&#8217;s a lot more I want to build:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Custom viewport dimensions<\/li>\n\n\n\n<li>Per-route thresholds<\/li>\n\n\n\n<li>Component-level capture targeting<\/li>\n\n\n\n<li>Visual diff image generation (overlay\/side-by-side in PR comments)<\/li>\n\n\n\n<li>Support for non-Ubuntu runners<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">If you&#8217;re dealing with visual regression headaches in your CI pipeline, give SnapDrift a try. The setup takes 10 minutes, and the worst case is you run <code>report-only<\/code> for a week and decide it&#8217;s not for you.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>GitHub:<\/strong> <a href=\"https:\/\/github.com\/ranacseruet\/snapdrift\" target=\"_blank\" rel=\"noreferrer noopener\">github.com\/ranacseruet\/snapdrift<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Star it if you find it useful, and open an issue if something breaks. Contributions are welcome \u2014 the <a href=\"https:\/\/github.com\/ranacseruet\/snapdrift\/blob\/main\/CONTRIBUTING.md\" target=\"_blank\" rel=\"noreferrer noopener\">Contributing Guide<\/a> has everything you need to get started. \ud83d\ude80<\/p>\n","protected":false},"excerpt":{"rendered":"<p>SnapDrift is a free, open-source visual regression testing tool built for GitHub Actions. It captures full-page screenshots on every push, compares them pixel-by-pixel against your baseline, and reports drift directly in your pull requests. No SaaS dashboards, no per-screenshot pricing \u2014 just two Actions and a JSON config.<\/p>\n","protected":false},"author":1,"featured_media":59810,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_genesis_hide_title":false,"_genesis_hide_breadcrumbs":false,"_genesis_hide_singular_image":false,"_genesis_hide_footer_widgets":false,"_genesis_custom_body_class":"","_genesis_custom_post_class":"","_genesis_layout":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[32,3262],"tags":[27,3284],"class_list":["post-59809","post","type-post","status-publish","format-standard","has-post-thumbnail","category-devops","category-projects","tag-github","tag-testing","entry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v28.4 (Yoast SEO v28.4) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>SnapDrift: Free Auto Visual Regression Testing On GitHub Actions - CodeSamplez.com<\/title>\n<meta name=\"description\" content=\"SnapDrift is a free, open-source visual regression testing tool for GitHub Actions. Catch UI drift directly in pull requests effortlessly.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/codesamplez.com\/devops\/visual-regression-testing-github-actions\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"SnapDrift: Free Auto Visual Regression Testing On GitHub Actions\" \/>\n<meta property=\"og:description\" content=\"SnapDrift is a free, open-source visual regression testing tool for GitHub Actions. Catch UI drift directly in pull requests effortlessly.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/codesamplez.com\/devops\/visual-regression-testing-github-actions\" \/>\n<meta property=\"og:site_name\" content=\"CodeSamplez.com\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/codesamplez\" \/>\n<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/ranacseruet\" \/>\n<meta property=\"article:published_time\" content=\"2026-03-20T00:32:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-03-20T00:32:04+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/codesamplez.com\/wp-content\/uploads\/2026\/03\/snapdrift-logo-banner.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"400\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Rana Ahsan\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@ranacseruet\" \/>\n<meta name=\"twitter:site\" content=\"@codesamplez\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Rana Ahsan\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":[\"Article\",\"BlogPosting\"],\"@id\":\"https:\\\/\\\/codesamplez.com\\\/devops\\\/visual-regression-testing-github-actions#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/codesamplez.com\\\/devops\\\/visual-regression-testing-github-actions\"},\"author\":{\"name\":\"Rana Ahsan\",\"@id\":\"https:\\\/\\\/codesamplez.com\\\/#\\\/schema\\\/person\\\/a82c3c07205f4bb73d6b3b0906bc328b\"},\"headline\":\"SnapDrift: Free Auto Visual Regression Testing On GitHub Actions\",\"datePublished\":\"2026-03-20T00:32:00+00:00\",\"dateModified\":\"2026-03-20T00:32:04+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/codesamplez.com\\\/devops\\\/visual-regression-testing-github-actions\"},\"wordCount\":1137,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/codesamplez.com\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/codesamplez.com\\\/devops\\\/visual-regression-testing-github-actions#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/codesamplez.com\\\/wp-content\\\/uploads\\\/2026\\\/03\\\/snapdrift-logo-banner.png\",\"keywords\":[\"github\",\"testing\"],\"articleSection\":[\"DevOps\",\"Projects\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/codesamplez.com\\\/devops\\\/visual-regression-testing-github-actions#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/codesamplez.com\\\/devops\\\/visual-regression-testing-github-actions\",\"url\":\"https:\\\/\\\/codesamplez.com\\\/devops\\\/visual-regression-testing-github-actions\",\"name\":\"SnapDrift: Free Auto Visual Regression Testing On GitHub Actions - CodeSamplez.com\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/codesamplez.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/codesamplez.com\\\/devops\\\/visual-regression-testing-github-actions#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/codesamplez.com\\\/devops\\\/visual-regression-testing-github-actions#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/codesamplez.com\\\/wp-content\\\/uploads\\\/2026\\\/03\\\/snapdrift-logo-banner.png\",\"datePublished\":\"2026-03-20T00:32:00+00:00\",\"dateModified\":\"2026-03-20T00:32:04+00:00\",\"description\":\"SnapDrift is a free, open-source visual regression testing tool for GitHub Actions. Catch UI drift directly in pull requests effortlessly.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/codesamplez.com\\\/devops\\\/visual-regression-testing-github-actions#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/codesamplez.com\\\/devops\\\/visual-regression-testing-github-actions\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/codesamplez.com\\\/devops\\\/visual-regression-testing-github-actions#primaryimage\",\"url\":\"https:\\\/\\\/codesamplez.com\\\/wp-content\\\/uploads\\\/2026\\\/03\\\/snapdrift-logo-banner.png\",\"contentUrl\":\"https:\\\/\\\/codesamplez.com\\\/wp-content\\\/uploads\\\/2026\\\/03\\\/snapdrift-logo-banner.png\",\"width\":1200,\"height\":400,\"caption\":\"visual diff github\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/codesamplez.com\\\/devops\\\/visual-regression-testing-github-actions#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/codesamplez.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"SnapDrift: Free Auto Visual Regression Testing On GitHub Actions\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/codesamplez.com\\\/#website\",\"url\":\"https:\\\/\\\/codesamplez.com\\\/\",\"name\":\"CODESAMPLEZ.COM\",\"description\":\"Programming And Development Resources\",\"publisher\":{\"@id\":\"https:\\\/\\\/codesamplez.com\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/codesamplez.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/codesamplez.com\\\/#organization\",\"name\":\"codesamplez.com\",\"url\":\"https:\\\/\\\/codesamplez.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/codesamplez.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/codesamplez.com\\\/wp-content\\\/uploads\\\/2024\\\/10\\\/cropped-favicon.webp\",\"contentUrl\":\"https:\\\/\\\/codesamplez.com\\\/wp-content\\\/uploads\\\/2024\\\/10\\\/cropped-favicon.webp\",\"width\":512,\"height\":512,\"caption\":\"codesamplez.com\"},\"image\":{\"@id\":\"https:\\\/\\\/codesamplez.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/codesamplez\",\"https:\\\/\\\/x.com\\\/codesamplez\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/codesamplez.com\\\/#\\\/schema\\\/person\\\/a82c3c07205f4bb73d6b3b0906bc328b\",\"name\":\"Rana Ahsan\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/5c7a4f88bcf4a55cd1483386318ebecf27359154275a0b355b0ea186676f9f7f?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/5c7a4f88bcf4a55cd1483386318ebecf27359154275a0b355b0ea186676f9f7f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/5c7a4f88bcf4a55cd1483386318ebecf27359154275a0b355b0ea186676f9f7f?s=96&d=mm&r=g\",\"caption\":\"Rana Ahsan\"},\"description\":\"Rana Ahsan is a seasoned software engineer and technology leader specialized in distributed systems and software architecture. With a Master\u2019s in Software Engineering from Concordia University, his experience spans leading scalable architecture at Coursera and TopHat, contributing to open-source projects. This blog, CodeSamplez.com, showcases his passion for sharing practical insights on programming and distributed systems concepts and help educate others. Github | X | LinkedIn\",\"sameAs\":[\"https:\\\/\\\/github.com\\\/ranacseruet\",\"https:\\\/\\\/www.facebook.com\\\/ranacseruet\",\"https:\\\/\\\/www.linkedin.com\\\/in\\\/ranacseruet\\\/\",\"https:\\\/\\\/x.com\\\/ranacseruet\"],\"url\":\"https:\\\/\\\/codesamplez.com\\\/author\\\/admin\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"SnapDrift: Free Auto Visual Regression Testing On GitHub Actions - CodeSamplez.com","description":"SnapDrift is a free, open-source visual regression testing tool for GitHub Actions. Catch UI drift directly in pull requests effortlessly.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/codesamplez.com\/devops\/visual-regression-testing-github-actions","og_locale":"en_US","og_type":"article","og_title":"SnapDrift: Free Auto Visual Regression Testing On GitHub Actions","og_description":"SnapDrift is a free, open-source visual regression testing tool for GitHub Actions. Catch UI drift directly in pull requests effortlessly.","og_url":"https:\/\/codesamplez.com\/devops\/visual-regression-testing-github-actions","og_site_name":"CodeSamplez.com","article_publisher":"https:\/\/www.facebook.com\/codesamplez","article_author":"https:\/\/www.facebook.com\/ranacseruet","article_published_time":"2026-03-20T00:32:00+00:00","article_modified_time":"2026-03-20T00:32:04+00:00","og_image":[{"width":1200,"height":400,"url":"https:\/\/codesamplez.com\/wp-content\/uploads\/2026\/03\/snapdrift-logo-banner.png","type":"image\/png"}],"author":"Rana Ahsan","twitter_card":"summary_large_image","twitter_creator":"@ranacseruet","twitter_site":"@codesamplez","twitter_misc":{"Written by":"Rana Ahsan","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":["Article","BlogPosting"],"@id":"https:\/\/codesamplez.com\/devops\/visual-regression-testing-github-actions#article","isPartOf":{"@id":"https:\/\/codesamplez.com\/devops\/visual-regression-testing-github-actions"},"author":{"name":"Rana Ahsan","@id":"https:\/\/codesamplez.com\/#\/schema\/person\/a82c3c07205f4bb73d6b3b0906bc328b"},"headline":"SnapDrift: Free Auto Visual Regression Testing On GitHub Actions","datePublished":"2026-03-20T00:32:00+00:00","dateModified":"2026-03-20T00:32:04+00:00","mainEntityOfPage":{"@id":"https:\/\/codesamplez.com\/devops\/visual-regression-testing-github-actions"},"wordCount":1137,"commentCount":0,"publisher":{"@id":"https:\/\/codesamplez.com\/#organization"},"image":{"@id":"https:\/\/codesamplez.com\/devops\/visual-regression-testing-github-actions#primaryimage"},"thumbnailUrl":"https:\/\/codesamplez.com\/wp-content\/uploads\/2026\/03\/snapdrift-logo-banner.png","keywords":["github","testing"],"articleSection":["DevOps","Projects"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/codesamplez.com\/devops\/visual-regression-testing-github-actions#respond"]}]},{"@type":"WebPage","@id":"https:\/\/codesamplez.com\/devops\/visual-regression-testing-github-actions","url":"https:\/\/codesamplez.com\/devops\/visual-regression-testing-github-actions","name":"SnapDrift: Free Auto Visual Regression Testing On GitHub Actions - CodeSamplez.com","isPartOf":{"@id":"https:\/\/codesamplez.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/codesamplez.com\/devops\/visual-regression-testing-github-actions#primaryimage"},"image":{"@id":"https:\/\/codesamplez.com\/devops\/visual-regression-testing-github-actions#primaryimage"},"thumbnailUrl":"https:\/\/codesamplez.com\/wp-content\/uploads\/2026\/03\/snapdrift-logo-banner.png","datePublished":"2026-03-20T00:32:00+00:00","dateModified":"2026-03-20T00:32:04+00:00","description":"SnapDrift is a free, open-source visual regression testing tool for GitHub Actions. Catch UI drift directly in pull requests effortlessly.","breadcrumb":{"@id":"https:\/\/codesamplez.com\/devops\/visual-regression-testing-github-actions#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/codesamplez.com\/devops\/visual-regression-testing-github-actions"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/codesamplez.com\/devops\/visual-regression-testing-github-actions#primaryimage","url":"https:\/\/codesamplez.com\/wp-content\/uploads\/2026\/03\/snapdrift-logo-banner.png","contentUrl":"https:\/\/codesamplez.com\/wp-content\/uploads\/2026\/03\/snapdrift-logo-banner.png","width":1200,"height":400,"caption":"visual diff github"},{"@type":"BreadcrumbList","@id":"https:\/\/codesamplez.com\/devops\/visual-regression-testing-github-actions#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/codesamplez.com\/"},{"@type":"ListItem","position":2,"name":"SnapDrift: Free Auto Visual Regression Testing On GitHub Actions"}]},{"@type":"WebSite","@id":"https:\/\/codesamplez.com\/#website","url":"https:\/\/codesamplez.com\/","name":"CODESAMPLEZ.COM","description":"Programming And Development Resources","publisher":{"@id":"https:\/\/codesamplez.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/codesamplez.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/codesamplez.com\/#organization","name":"codesamplez.com","url":"https:\/\/codesamplez.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/codesamplez.com\/#\/schema\/logo\/image\/","url":"https:\/\/codesamplez.com\/wp-content\/uploads\/2024\/10\/cropped-favicon.webp","contentUrl":"https:\/\/codesamplez.com\/wp-content\/uploads\/2024\/10\/cropped-favicon.webp","width":512,"height":512,"caption":"codesamplez.com"},"image":{"@id":"https:\/\/codesamplez.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/codesamplez","https:\/\/x.com\/codesamplez"]},{"@type":"Person","@id":"https:\/\/codesamplez.com\/#\/schema\/person\/a82c3c07205f4bb73d6b3b0906bc328b","name":"Rana Ahsan","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/5c7a4f88bcf4a55cd1483386318ebecf27359154275a0b355b0ea186676f9f7f?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/5c7a4f88bcf4a55cd1483386318ebecf27359154275a0b355b0ea186676f9f7f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/5c7a4f88bcf4a55cd1483386318ebecf27359154275a0b355b0ea186676f9f7f?s=96&d=mm&r=g","caption":"Rana Ahsan"},"description":"Rana Ahsan is a seasoned software engineer and technology leader specialized in distributed systems and software architecture. With a Master\u2019s in Software Engineering from Concordia University, his experience spans leading scalable architecture at Coursera and TopHat, contributing to open-source projects. This blog, CodeSamplez.com, showcases his passion for sharing practical insights on programming and distributed systems concepts and help educate others. Github | X | LinkedIn","sameAs":["https:\/\/github.com\/ranacseruet","https:\/\/www.facebook.com\/ranacseruet","https:\/\/www.linkedin.com\/in\/ranacseruet\/","https:\/\/x.com\/ranacseruet"],"url":"https:\/\/codesamplez.com\/author\/admin"}]}},"jetpack_publicize_connections":[],"jetpack_shortlink":"https:\/\/wp.me\/p1hHlI-fyF","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":24625,"url":"https:\/\/codesamplez.com\/devops\/github-actions-tutorial","url_meta":{"origin":59809,"position":0},"title":"GitHub Actions Tutorial: Automating Your Deployment Workflow","author":"Rana Ahsan","date":"September 7, 2014","format":false,"excerpt":"Automation has transformed how developers handle deployments. GitHub Actions provides a powerful CI\/CD platform built directly into GitHub, eliminating the need for external systems. In this tutorial, I'll show you exactly how to create automated workflows that will save you countless hours and reduce deployment errors.","rel":"","context":"In &quot;DevOps&quot;","block_context":{"text":"DevOps","link":"https:\/\/codesamplez.com\/category\/devops"},"img":{"alt_text":"Github Actions Tutorial","src":"https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2014\/09\/github-actions-tutorial.webp?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2014\/09\/github-actions-tutorial.webp?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2014\/09\/github-actions-tutorial.webp?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2014\/09\/github-actions-tutorial.webp?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2014\/09\/github-actions-tutorial.webp?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2014\/09\/github-actions-tutorial.webp?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":26477,"url":"https:\/\/codesamplez.com\/development\/unit-testing-in-nodejs","url_meta":{"origin":59809,"position":1},"title":"Node.js Unit Testing: The Ultimate Guide","author":"Rana Ahsan","date":"January 7, 2015","format":false,"excerpt":"Getting started with unit testing in Node.js is simple: pick a testing framework like Mocha paired with Chai, install them via npm, and write your first test using \"describe\" and \"it\" blocks . Running mocha will execute your tests, showing green checkmarks for passes and helping you ship more robust\u2026","rel":"","context":"In &quot;Development&quot;","block_context":{"text":"Development","link":"https:\/\/codesamplez.com\/category\/development"},"img":{"alt_text":"NodeJS Unit Testing","src":"https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2015\/01\/nodejs-unit-testing-1.webp?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2015\/01\/nodejs-unit-testing-1.webp?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2015\/01\/nodejs-unit-testing-1.webp?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2015\/01\/nodejs-unit-testing-1.webp?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2015\/01\/nodejs-unit-testing-1.webp?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2015\/01\/nodejs-unit-testing-1.webp?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":58380,"url":"https:\/\/codesamplez.com\/devops\/aws-sam-beginners-guide","url_meta":{"origin":59809,"position":2},"title":"AWS SAM: Build And Deploy Lambda Effortlessly","author":"Rana Ahsan","date":"April 21, 2025","format":false,"excerpt":"Kick\u2011start your serverless journey with this hands\u2011on AWS\u00a0SAM Tutorial. Learn why SAM matters, scaffold your first Lambda in minutes, test locally with Docker, and automate deployments using GitHub\u00a0Actions. Packed with code samples, real\u2011world tips, and troubleshooting hacks from a veteran dev, this guide turns beginners into confident builders.","rel":"","context":"In &quot;DevOps&quot;","block_context":{"text":"DevOps","link":"https:\/\/codesamplez.com\/category\/devops"},"img":{"alt_text":"AWS SAM Tutorial For Beginners","src":"https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2025\/04\/aws-sam.webp?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2025\/04\/aws-sam.webp?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2025\/04\/aws-sam.webp?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2025\/04\/aws-sam.webp?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2025\/04\/aws-sam.webp?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2025\/04\/aws-sam.webp?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":25555,"url":"https:\/\/codesamplez.com\/projects\/multi-process-clustered-app-nodejs-server","url_meta":{"origin":59809,"position":3},"title":"Create Multi-Process NodeJS Server With &#8216;Clustered-Node&#8217;","author":"Rana Ahsan","date":"November 9, 2014","format":false,"excerpt":"Notice: This library is Outdated and haven't been updated for a long time. Fork the repository and update as per your own need. If you are a NodeJS application developer, you are developing it in a single-threaded\/single-process-based manner by default. If you deploy it on a multi-CPU\/core-based server, you are\u2026","rel":"","context":"In &quot;Projects&quot;","block_context":{"text":"Projects","link":"https:\/\/codesamplez.com\/category\/projects"},"img":{"alt_text":"clustered node","src":"https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2014\/11\/clustered-node.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":24486,"url":"https:\/\/codesamplez.com\/development\/phpunit-tutorial-beginners","url_meta":{"origin":59809,"position":4},"title":"PHPUnit Tutorial For Beginners: The Complete Guide","author":"Rana Ahsan","date":"July 23, 2014","format":false,"excerpt":"This beginner-friendly tutorial introduces PHPUnit, a popular unit testing framework for PHP. It guides readers through setting up PHPUnit, writing basic test cases, and understanding assertions. With clear examples and step-by-step instructions, it's an ideal starting point for developers aiming to implement automated testing in their PHP applications.","rel":"","context":"In &quot;Development&quot;","block_context":{"text":"Development","link":"https:\/\/codesamplez.com\/category\/development"},"img":{"alt_text":"PHPUnit Tutorial For Beginners","src":"https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2014\/07\/phpunit.webp?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2014\/07\/phpunit.webp?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2014\/07\/phpunit.webp?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2014\/07\/phpunit.webp?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2014\/07\/phpunit.webp?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2014\/07\/phpunit.webp?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":59581,"url":"https:\/\/codesamplez.com\/productivity\/best-ai-coding-agents","url_meta":{"origin":59809,"position":5},"title":"Best AI Coding Agents in 2026: The Complete Beginner&#8217;s Guide","author":"Rana Ahsan","date":"February 18, 2026","format":false,"excerpt":"The best AI coding agents in 2026 don't just autocomplete your lines \u2014 they plan, execute, and debug entire features autonomously. Whether you're a weekend hacker or a seasoned engineer, this guide breaks down Google Antigravity, Claude Code, OpenAI Codex, OpenCode, and KiloCode so you can pick the right tool\u2026","rel":"","context":"In &quot;Productivity&quot;","block_context":{"text":"Productivity","link":"https:\/\/codesamplez.com\/category\/productivity"},"img":{"alt_text":"Best AI Coding Agents","src":"https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2026\/02\/best-ai-coding-agents.webp?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2026\/02\/best-ai-coding-agents.webp?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2026\/02\/best-ai-coding-agents.webp?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2026\/02\/best-ai-coding-agents.webp?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2026\/02\/best-ai-coding-agents.webp?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/codesamplez.com\/wp-content\/uploads\/2026\/02\/best-ai-coding-agents.webp?resize=1400%2C800&ssl=1 4x"},"classes":[]}],"jetpack_featured_media_url":"https:\/\/codesamplez.com\/wp-content\/uploads\/2026\/03\/snapdrift-logo-banner.png","_links":{"self":[{"href":"https:\/\/codesamplez.com\/wp-json\/wp\/v2\/posts\/59809","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/codesamplez.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codesamplez.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codesamplez.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codesamplez.com\/wp-json\/wp\/v2\/comments?post=59809"}],"version-history":[{"count":3,"href":"https:\/\/codesamplez.com\/wp-json\/wp\/v2\/posts\/59809\/revisions"}],"predecessor-version":[{"id":59813,"href":"https:\/\/codesamplez.com\/wp-json\/wp\/v2\/posts\/59809\/revisions\/59813"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/codesamplez.com\/wp-json\/wp\/v2\/media\/59810"}],"wp:attachment":[{"href":"https:\/\/codesamplez.com\/wp-json\/wp\/v2\/media?parent=59809"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codesamplez.com\/wp-json\/wp\/v2\/categories?post=59809"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codesamplez.com\/wp-json\/wp\/v2\/tags?post=59809"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}