πΈ github-screenshot-action
Capture, monitor, and version website screenshots β automatically.
A reusable GitHub Action that takes screenshots of websites from a JSON list, with parallel execution, retry logic, cron scheduling, and automated PR creation.
β¨ Features
| Feature | Description |
|---|---|
| π JSON-driven | Define all your target sites in a simple JSON file β no code changes needed |
| β‘ Parallel execution | Configurable concurrency to capture multiple sites simultaneously |
| π Retry & timeout | Automatically retries failed captures with configurable limits |
| π Cron scheduling | Run on any schedule to monitor visual changes over time |
| πΏ Branch isolation | Screenshots are committed to a dedicated branch, keeping main clean |
| π Automated PRs | Optionally open a pull request automatically after each monitoring run |
| π³ Pre-built Docker image | No cold build β uses a pre-published image from GHCR for fast startup |
| π Chromium-based | Full Puppeteer + Chromium stack for accurate, real-browser rendering |
| β³ Wait strategies | Wait for full page load, network idle, or DOM ready before capturing |
| π₯ Square mode | Optionally clip output to a square for thumbnails or social previews |
π Quick Start
1. Create your sites file
Add a sites.json file to your repository:
[
{ "name": "homepage", "url": "https://example.com" },
{ "name": "dashboard", "url": "https://app.example.com/dashboard" },
{ "name": "pricing", "url": "https://example.com/pricing" }
]
Each entry requires a url and a name. The name becomes the screenshot filename (e.g. homepage.png).
2. Create your workflow
name: Website monitoring
on:
schedule:
- cron: "0 */6 * * *" # every 6 hours
workflow_dispatch:
jobs:
monitor:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run screenshot monitoring
uses: guibranco/github-screenshot-action@v2.0.17
with:
json_file: "sites.json"
output_dir: "screenshots/"
concurrency: "5"
retries: "2"
timeout_ms: "20000"
create_pr: "true"
branch_name: "monitor/screenshots"
wait_until: "networkidle0"
square: "false"
viewport_width: "1280"
env:
GITHUB_TOKEN: $
βοΈ Inputs
| Input | Required | Default | Description |
|---|---|---|---|
json_file |
β | screenshots.json |
Path to the JSON file listing sites to capture |
output_dir |
β | screenshots |
Directory where .png files will be saved |
concurrency |
β | 3 |
Number of screenshots to take in parallel |
retries |
β | 2 |
How many times to retry a failed screenshot |
timeout_ms |
β | 30000 |
Page load timeout per site in milliseconds |
create_pr |
β | false |
If true, opens a PR after committing screenshots |
branch_name |
β | monitor/screenshots |
Branch to commit screenshots to |
wait_until |
β | load |
Page load event to wait for before capturing. See Wait Strategies |
square |
β | false |
If true, clips the output to a square using viewport_width as the side length |
viewport_width |
β | 1280 |
Viewport width in pixels. Also controls the square size when square is true |
π JSON File Format
[
{
"name": "landing-page",
"url": "https://example.com"
},
{
"name": "login",
"url": "https://example.com/login"
}
]
nameβ used as the output filename (name.png). Use lowercase, hyphen-separated values for clean filenames.urlβ full URL to capture. If the protocol is omitted,https://is prepended automatically.
π PR Automation
When create_pr: "true", the action will:
- Check out (or create) the branch specified in
branch_name - Capture all screenshots and write them to
output_dir - Commit any changed or new
.pngfiles with[skip ci]to avoid re-triggering workflows - Force-push the branch
- Open a pull request against
mainβ or skip silently if a PR already exists
This gives you a clean, reviewable diff of visual changes over time.
Note: The workflow needs
GITHUB_TOKENpassed viaenvfor PR creation and branch push to work.
env:
GITHUB_TOKEN: $
β³ Wait Strategies
The wait_until input controls when Puppeteer considers the page ready to capture. Choose based on how dynamic the target site is:
| Value | Waits until⦠| Best for |
|---|---|---|
domcontentloaded |
HTML is parsed, no assets waited on | Simple static pages |
load |
All resources loaded (default) | Most standard websites |
networkidle2 |
No more than 2 requests in flight for 500ms | Pages with background polling |
networkidle0 |
Zero network requests for 500ms | SPAs and lazy-loaded content |
Tip:
networkidle0produces the most complete captures but is the slowest. If a site has continuous background requests (analytics, websockets), usenetworkidle2to avoid hitting the timeout.
π₯ Square Mode
When square: "true", the action sets a square viewport and clips the output to viewport_width Γ viewport_width pixels. Full-page scrolling is disabled in this mode.
square: "true"
viewport_width: "1280" # produces a 1280Γ1280 PNG
This is useful for generating consistent thumbnails, social preview images, or monitoring dashboards where uniform dimensions are required.
Note:
squareand full-page capture are mutually exclusive. Whensquareis enabled, only the top portion of the page visible within the square viewport is captured.
ποΈ Scheduling Examples
# Every 6 hours
- cron: "0 */6 * * *"
# Once a day at midnight UTC
- cron: "0 0 * * *"
# Every Monday at 8am UTC
- cron: "0 8 * * 1"
# Every hour during business hours (9β17) on weekdays
- cron: "0 9-17 * * 1-5"
ποΈ Architecture
sites.json
β
βΌ
parser.ts βββΊ loadItems()
β
βΌ
screenshot.ts βββΊ Puppeteer + pLimit (parallel)
β β
β setViewport(width, height)
β goto(url, { waitUntil })
β screenshot({ fullPage | clip })
β
βΌ
output_dir/*.png
β
βΌ
git.ts βββΊ checkout branch
β git add -f / commit
β git push --force
β GitHub API PR (optional)
βΌ
Pull Request / Branch
π³ Docker Image
This action uses a pre-built Docker image published to GitHub Container Registry, so there is no build step at runtime.
ghcr.io/guibranco/github-screenshot-action:<version>
The image includes:
node:24-slimbase- Chromium and all required system dependencies
- Pre-compiled TypeScript output in
dist/
New images are published automatically on every release via the release.yml workflow.
π Permissions
Your workflow needs the following permissions for full functionality:
permissions:
contents: write # to push the screenshot branch
pull-requests: write # to open PRs
If you are using a classic GITHUB_TOKEN without an explicit permissions block, make sure your repositoryβs Actions settings allow workflows to create pull requests.
π οΈ Development
Prerequisites
- Node.js 24+
- npm
Setup
git clone https://github.com/guibranco/github-screenshot-action.git
cd github-screenshot-action
npm install
Build
npm run build
Output is written to dist/.
Project Structure
βββ src/
β βββ main.ts # Entry point β orchestrates the full run
β βββ parser.ts # Reads and validates sites.json
β βββ screenshot.ts # Puppeteer screenshot logic with concurrency
β βββ git.ts # Git operations: commit, branch, PR creation
β βββ logger.ts # Emoji-prefixed console logger
βββ Dockerfile # Pre-built image definition
βββ entrypoint.sh # Docker entrypoint
βββ action.yml # Action metadata
βββ sites.json # Example sites file
Publishing a new version
- Merge your changes to
main - The
release.ymlworkflow automatically determines the next version via GitVersion - It builds and pushes the Docker image to GHCR tagged as
vX.Y.Zandlatest - It opens a PR updating the
image:tag inaction.ymlβ merge it to complete the release
π License
MIT Β© Guilherme Branco Stracini
Made with β€οΈ and β β contributions welcome via issues and pull requests.