Static site generator plugin for Vizzly - seamlessly integrate static sites (Gatsby, Astro, Jekyll, Next.js) into your visual development workflow
npm install @vizzly-testing/static-siteSeamlessly integrate your static sites (Gatsby, Astro, Jekyll, Next.js static export, etc.) into Vizzly's visual development workflow. Iterate locally with vizzly tdd, automatically create team builds from CI/CD, and collaborate on visual changes with position-based comments and review rules.
``bash`
npm install @vizzly-testing/static-site
The plugin is automatically discovered by the Vizzly CLI via the @vizzly-testing/* scope.
`bashCapture screenshots from a static site build
vizzly static-site ./dist
$3
`javascript
import { run } from '@vizzly-testing/static-site';await run('./dist', {
viewports: 'mobile:375x667,desktop:1920x1080',
concurrency: 3,
}, {
logger: console,
config: vizzlyConfig,
services: serviceContainer,
});
`Configuration
$3
Add a
staticSite section to your vizzly.config.js:`javascript
// vizzly.config.js
export default {
// Standard Vizzly config
server: { port: 47392 },
build: { environment: 'test' }, // Static Site plugin config
staticSite: {
viewports: [
{ name: 'mobile', width: 375, height: 667 },
{ name: 'tablet', width: 768, height: 1024 },
{ name: 'desktop', width: 1920, height: 1080 },
],
browser: {
headless: true,
args: ['--no-sandbox'],
},
screenshot: {
fullPage: false,
omitBackground: false,
},
// Concurrency auto-detected from CPU cores (min 2, max 8)
// concurrency: 4,
// Page filtering
include: 'blog/**',
exclude: '**/404.html',
// Page discovery
pageDiscovery: {
useSitemap: true,
sitemapPath: 'sitemap.xml',
scanHtml: true,
},
},
};
`$3
For page-specific interactions and overrides, create a
vizzly.static-site.js file:`javascript
// vizzly.static-site.js
export default {
// Interaction hooks - run before screenshots
interactions: {
'blog/*': async (page) => {
await page.waitForSelector('.blog-content');
},
'products/*': async (page) => {
await page.click('.view-details');
},
}, // Per-page configuration overrides
pages: {
'/': {
viewports: ['mobile', 'desktop'],
},
'/pricing': {
screenshot: { fullPage: true },
},
},
};
`Configuration Priority
Configuration is merged in this order (later overrides earlier):
1. Default configuration
2.
config.staticSite from vizzly.config.js
3. vizzly.static-site.js interactions and pages
4. CLI optionsCLI Options
-
--viewports - Comma-separated viewport definitions (format: name:WxH)
- --concurrency - Number of parallel browser tabs (default: auto-detected based on CPU cores, min 2, max 8)
- --include - Include page pattern (glob)
- --exclude - Exclude page pattern (glob)
- --browser-args - Additional Puppeteer browser arguments
- --headless - Run browser in headless mode (default: true)
- --full-page - Capture full page screenshots (default: false)
- --timeout - Screenshot timeout in milliseconds (default: 45000)
- --dry-run - Print discovered pages and task count without capturing screenshots
- --use-sitemap - Use sitemap.xml for page discovery (default: true)
- --sitemap-path - Path to sitemap.xml relative to build directoryPage Discovery
The plugin discovers pages using two methods:
$3
Automatically discovers pages from sitemap.xml if available:
`xml
https://example.com/
https://example.com/blog/post-1
`$3
Recursively scans the build directory for .html files.
Both methods can be used together - the plugin will merge and deduplicate pages.
Interaction Hooks
Interaction hooks allow you to interact with pages before capturing screenshots. Define these in
vizzly.static-site.js:$3
`javascript
// vizzly.static-site.js
export default {
interactions: {
'blog/*': async (page) => {
// Apply to all blog pages
await page.waitForSelector('.blog-content');
},
'products/*': async (page) => {
// Apply to all product pages
await page.click('.view-details');
await page.waitForSelector('.product-modal');
},
'/': async (page) => {
// Specific page
await page.evaluate(() => window.scrollTo(0, 500));
},
},
};
`$3
Create reusable hooks that can be referenced in page configs:
`javascript
// vizzly.static-site.js
export default {
interactions: {
'scroll-to-footer': async (page) => {
await page.evaluate(() => {
window.scrollTo(0, document.body.scrollHeight);
});
},
},
pages: {
'/contact': {
interaction: 'scroll-to-footer',
},
},
};
`Pattern Matching
Patterns support glob-like syntax:
-
* - Match any characters except /
- ** - Match any characters including /
- blog/* - Match all blog pages
- docs/** - Match all pages under docs
- */.html - Match all HTML filesScreenshot Naming
Screenshots are named based on the page path, with viewport information stored as properties for better grouping:
Name format:
path-to-page (slashes replaced with hyphens)Properties: Viewport metadata (
viewport, viewportWidth, viewportHeight)Examples:
- Name:
index, Properties: { viewport: 'mobile', viewportWidth: 375, viewportHeight: 667 }
- Name: blog-post-1, Properties: { viewport: 'desktop', viewportWidth: 1920, viewportHeight: 1080 }
- Name: docs-getting-started, Properties: { viewport: 'tablet', viewportWidth: 768, viewportHeight: 1024 }This approach allows Vizzly to group screenshots by viewport while keeping names clean and compatible with file system restrictions.
Visual Development Workflow
This plugin integrates static sites into Vizzly's visual development workflow, enabling both local TDD iteration and seamless team collaboration. The plugin automatically detects which mode to use:
$3
When a TDD server is running, screenshots are compared locally for fast iteration:
`bash
Start TDD server
vizzly tdd startCapture screenshots (automatically uses TDD mode)
vizzly static-site ./distView live results at http://localhost:47392
`Output:
`
ā¹ š TDD mode: Using local server
ā¹ š Found 12 pages in ./dist
ā¹ ā /@default
ā¹ ā /blog/post-1@default
ā¹ ā
Captured 12 screenshots successfully
`$3
When a
VIZZLY_TOKEN is set, screenshots are uploaded to the cloud for team review:`bash
Capture and upload to Vizzly cloud (automatically uses Run mode)
VIZZLY_TOKEN=your-token vizzly static-site ./dist
`Output:
`
ā¹ āļø Run mode: Uploading to cloud
ā¹ š https://app.vizzly.dev/your-org/project/builds/...
ā¹ š Found 12 pages in ./dist
ā¹ ā /@default
ā¹ ā
Captured 12 screenshots successfully
ā¹ š View results: https://app.vizzly.dev/your-org/project/builds/...
`$3
The plugin automatically chooses the mode:
1. TDD mode - If a TDD server is running (
.vizzly/server.json found)
2. Run mode - If VIZZLY_TOKEN environment variable is set
3. Warning - If neither is available, warns and skips screenshotsNo need to wrap with
vizzly run - the plugin handles everything!Supported Static Site Generators
- Gatsby - Build with
gatsby build, output in public/
- Astro - Build with astro build, output in dist/
- Jekyll - Build with jekyll build, output in _site/
- Next.js - Static export with next build && next export, output in out/
- Eleventy - Build with eleventy, output in _site/
- Hugo - Build with hugo, output in public/
- VuePress - Build with vuepress build, output in .vuepress/dist/
- Docusaurus - Build with docusaurus build, output in build/
- Any static site generator that produces HTML filesExample Workflows
$3
`bash
Build Gatsby site
npm run buildCapture screenshots
vizzly static-site ./public
`$3
`bash
Build Astro site
npm run buildCapture screenshots with custom viewports
vizzly static-site ./dist --viewports "mobile:375x667,desktop:1920x1080"
`$3
`bash
Build and export Next.js site
npm run buildCapture screenshots
vizzly static-site ./out
`$3
`bash
Build Jekyll site
bundle exec jekyll buildCapture only blog pages
vizzly static-site ./_site --include "blog/**"
`CI/CD Integration
`yaml
.github/workflows/visual-tests.yml
name: Visual Testson: [push, pull_request]
jobs:
visual-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
# Build your static site
- run: npm install
- run: npm run build
# Run visual tests
- run: npx @vizzly-testing/cli static-site ./dist
env:
VIZZLY_TOKEN: ${{ secrets.VIZZLY_TOKEN }}
`Troubleshooting
$3
Use
--dry-run to see which pages are discovered without capturing screenshots:
`bash
vizzly static-site ./dist --dry-run
`This shows pages grouped by source (sitemap vs HTML scan), the total screenshot count, and your current configuration.
Ensure your build has completed and check for sitemap.xml or HTML files:
`bash
ls dist/sitemap.xml
ls dist/*/.html
`$3
Try adding browser arguments:
`bash
vizzly static-site ./dist --browser-args "--no-sandbox,--disable-dev-shm-usage"
`$3
If your page needs time to render, use interaction hooks:
`javascript
interactions: {
'**': async (page) => {
await page.waitForSelector('.app-loaded', { visible: true });
},
}
`$3
The plugin automatically converts sitemap URLs to relative paths. If you have custom URL structures, use HTML scanning instead:
`javascript
pageDiscovery: {
useSitemap: false,
scanHtml: true,
}
``MIT Ā© Stubborn Mule Software