Scrnify blog

Automatically Generate Up-to-Date Documentation Screenshots with the Scrnify API

Updated 4/11/2025

Hey there! Laura and Heidi here from SCRNIFY! πŸ‘‹

Ever found yourself spending hours taking, cropping, and updating screenshots for your product documentation? We've been there tooβ€”manually capturing UI elements after each release, making sure everything is pixel-perfect, and then realizing you need to do it all over again after a minor UI change. It's tedious, time-consuming, and frankly, a bit soul-crushing. πŸ˜…

That's exactly why we built SCRNIFY, and in this article, we'll show you how to automate your documentation screenshots using our developer-friendly API. By the end, you'll have a robust system that automatically captures fresh screenshots whenever your UI changes, keeping your docs looking sharp and accurate with minimal effort!

Try the SCRNIFY open beta and review current pricing.

Why Automate Documentation Screenshots?

Before diving into the code, let's talk about why automating your documentation screenshots is a game-changer:

  • βœ… Accuracy: Screenshots always reflect the current state of your application
  • βœ… Consistency: Every screenshot has the same dimensions, quality, and style
  • βœ… Time-saving: No more manual capturing, cropping, and editing
  • βœ… Freshness: Documentation stays current with your latest UI changes
  • βœ… Developer happiness: Less tedious work means happier developers! πŸŽ‰

When your product evolves quickly, keeping documentation screenshots up-to-date becomes a significant burden. By automating this process, you can ensure your docs are never outdated, which leads to better user experience and fewer support tickets.

Prerequisites

Before we start building our screenshot automation system, make sure you have:

  • Node.js installed (v18 or higher recommended)
  • A Scrnify API key (sign up here during our open beta)
  • A list of URLs or app states you want to capture
  • Basic knowledge of JavaScript and async/await patterns

Setting Up Your Project

Let's start by creating a new Node.js project for our screenshot automation tool:

# Create a new directory and navigate into it
mkdir docs-screenshot-automation
cd docs-screenshot-automation

# Initialize a new Node.js project
npm init -y

# Create necessary directories
mkdir screenshots

Next, let's create a .env file to store our API key securely:

# .env
SCRNIFY_API_KEY=your_api_key_here

Now, install the dependencies we'll need:

npm install dotenv fs-extra axios

Basic Script Setup: Taking Your First Documentation Screenshots

Let's create our main script file index.js that will handle capturing screenshots of key pages or UI components:

// index.js
require('dotenv').config()
const fs = require('fs-extra')
const path = require('path')
const axios = require('axios')

// Configuration
const API_KEY = process.env.SCRNIFY_API_KEY
const BASE_URL = 'https://api.scrnify.com/capture'
const SCREENSHOTS_DIR = path.join(__dirname, 'screenshots')

// Ensure screenshots directory exists
fs.ensureDirSync(SCREENSHOTS_DIR)

// Define the pages/components to capture
const screenshotTargets = [
    {
        name: 'login-page',
        url: 'https://your-app.com/login',
        width: 1280,
        height: 800,
        fullPage: false,
    },
    {
        name: 'dashboard-overview',
        url: 'https://your-app.com/dashboard',
        width: 1280,
        fullPage: true,
    },
    {
        name: 'user-settings-mobile',
        url: 'https://your-app.com/settings',
        width: 375,
        height: 667,
        fullPage: false,
        userAgent:
            'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1',
    },
]

/**
 * Captures a screenshot using the Scrnify API
 * @param {Object} target - Screenshot target configuration
 * @returns {Promise<Buffer>} - Screenshot buffer
 */
async function captureScreenshot(target) {
    try {
        // Set up the parameters
        const params = new URLSearchParams({
            key: API_KEY,
            url: target.url,
            type: 'image',
            format: 'png',
            width: target.width.toString(),
            waitUntil: 'networkIdle', // Wait until page is fully loaded
        })

        // Add conditional parameters
        if (!target.fullPage) {
            params.append('height', target.height.toString())
        }

        if (target.fullPage) {
            params.append('fullPage', 'true')
        }

        if (target.userAgent) {
            params.append('userAgent', target.userAgent)
        }

        // Construct the full URL
        const requestUrl = `${BASE_URL}?${params.toString()}`

        console.log(`πŸš€ Capturing screenshot of ${target.name}...`)

        // Make the API request
        const response = await axios({
            method: 'get',
            url: requestUrl,
            responseType: 'arraybuffer',
        })

        // Save the image to a file
        const filePath = path.join(SCREENSHOTS_DIR, `${target.name}.png`)
        await fs.writeFile(filePath, response.data)

        console.log(`βœ… Screenshot saved to ${filePath}`)
        return response.data
    } catch (error) {
        console.error(`❌ Error capturing screenshot for ${target.name}:`, error.message)
        throw error
    }
}

/**
 * Captures all screenshots defined in the targets array
 */
async function captureAllScreenshots() {
    console.log(`πŸ“Έ Starting screenshot capture for ${screenshotTargets.length} targets...`)

    // Process screenshots in parallel
    const promises = screenshotTargets.map(target => captureScreenshot(target))

    try {
        await Promise.all(promises)
        console.log('πŸŽ‰ All screenshots captured successfully!')
    } catch (error) {
        console.error('❌ Error during screenshot capture process:', error.message)
    }
}

// Run the screenshot capture process
captureAllScreenshots()

This script does several important things:

  1. Loads your API key from the environment variables
  2. Defines an array of screenshot targets (pages or components)
  3. Creates a function to capture screenshots using the Scrnify API
  4. Processes all targets in parallel for efficiency
  5. Saves each screenshot with a meaningful filename

Run the script with:

node index.js

You'll see the script capture screenshots for each target and save them to the screenshots directory.

Customizing Screenshot Parameters

Scrnify's API offers several parameters to customize your screenshots. Here are some of the most useful ones for documentation:

Waiting for Page Load

Documentation screenshots should show the fully loaded state of your UI. The waitUntil parameter helps ensure this:

params.append('waitUntil', 'networkIdle') // Wait until network is idle

Other useful values include:

  • load: Wait for the load event
  • DOMContentLoaded: Wait for the DOM to be ready
  • firstMeaningfulPaint: Wait for the main content to be visible

Capturing Full-Page Screenshots

For documentation that needs to show entire pages, use the fullPage parameter:

params.append('fullPage', 'true')

This is perfect for capturing long-form content like settings pages, dashboards, or documentation pages themselves.

Different Device Viewports

For responsive documentation, you'll want to show how your UI looks on different devices:

// Mobile screenshot
{
  name: 'login-page-mobile',
  url: 'https://your-app.com/login',
  width: 375,
  height: 667,
  fullPage: false,
  userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1'
}

// Tablet screenshot
{
  name: 'login-page-tablet',
  url: 'https://your-app.com/login',
  width: 768,
  height: 1024,
  fullPage: false
}

// Desktop screenshot
{
  name: 'login-page-desktop',
  url: 'https://your-app.com/login',
  width: 1280,
  height: 800,
  fullPage: false
}

Handling Authentication for Protected Pages

For documentation screenshots of authenticated pages, you'll need to handle authentication separately. Since Scrnify doesn't directly support cookie injection, we can use URL parameters or special routes for authentication:

// Add this to your screenshotTargets array
{
  name: 'user-profile',
  url: 'https://your-app.com/profile?auth_token=demo_token',
  width: 1280,
  height: 800,
  fullPage: false
}

For more complex authentication flows, you could:

  1. Create a special route in your application that auto-authenticates with a demo account
  2. Use URL parameters to trigger auto-login for documentation purposes
  3. Set up a pre-authenticated demo environment specifically for documentation

Using Local Automation for Element Screenshots

While Scrnify is great for full-page and viewport screenshots, sometimes you need screenshots of specific UI elements. For these cases, we can combine Scrnify with a local Playwright script:

// element-screenshots.js
const {chromium} = require('playwright')
const fs = require('fs-extra')
const path = require('path')

// Ensure screenshots directory exists
const SCREENSHOTS_DIR = path.join(__dirname, 'screenshots')
fs.ensureDirSync(SCREENSHOTS_DIR)

// Define elements to capture
const elementTargets = [
    {
        name: 'login-button',
        url: 'https://your-app.com/login',
        selector: '.login-button',
        padding: 10, // Add padding around the element
    },
    {
        name: 'dropdown-menu',
        url: 'https://your-app.com/dashboard',
        selector: '.dropdown-menu',
        // First we need to click to open the dropdown
        beforeScreenshot: async page => {
            await page.click('.dropdown-trigger')
            await page.waitForSelector('.dropdown-menu', {state: 'visible'})
        },
    },
]

async function captureElementScreenshots() {
    const browser = await chromium.launch()
    const page = await browser.newPage()

    try {
        for (const target of elementTargets) {
            console.log(`πŸ“Έ Capturing element: ${target.name}`)

            // Navigate to the page
            await page.goto(target.url, {waitUntil: 'networkidle'})

            // Execute any pre-screenshot actions
            if (target.beforeScreenshot) {
                await target.beforeScreenshot(page)
            }

            // Find the element
            const element = await page.$(target.selector)
            if (!element) {
                console.error(`❌ Element not found: ${target.selector}`)
                continue
            }

            // Take the screenshot
            const filePath = path.join(SCREENSHOTS_DIR, `${target.name}.png`)
            await element.screenshot({
                path: filePath,
                padding: target.padding || 0,
            })

            console.log(`βœ… Element screenshot saved to ${filePath}`)
        }
    } catch (error) {
        console.error('❌ Error capturing element screenshots:', error)
    } finally {
        await browser.close()
    }
}

captureElementScreenshots()

This script complements the Scrnify-based approach by handling specific element screenshots locally. You'll need to install Playwright first:

npm install playwright

Integrating with CI/CD Pipelines

The real power of automated documentation screenshots comes when you integrate them with your CI/CD pipeline. This ensures your screenshots are always up-to-date with your latest code.

Here's how to integrate with GitHub Actions:

Create a file at .github/workflows/update-screenshots.yml:

name: Update Documentation Screenshots

on:
    push:
        branches: [main]
    # Run weekly to ensure screenshots stay fresh
    schedule:
        - cron: '0 0 * * 1' # Every Monday at midnight

jobs:
    update-screenshots:
        runs-on: ubuntu-latest

        steps:
            - uses: actions/checkout@v3

            - name: Set up Node.js
              uses: actions/setup-node@v3
              with:
                  node-version: '18'

            - name: Install dependencies
              run: npm ci

            - name: Generate screenshots
              run: node index.js
              env:
                  SCRNIFY_API_KEY: ${{ secrets.SCRNIFY_API_KEY }}

            - name: Commit updated screenshots
              uses: stefanzweifel/git-auto-commit-action@v4
              with:
                  commit_message: 'docs: update documentation screenshots'
                  file_pattern: screenshots/*.png

This workflow:

  1. Runs whenever you push to the main branch or weekly on Monday
  2. Sets up Node.js and installs dependencies
  3. Runs your screenshot script with the API key from GitHub Secrets
  4. Commits the updated screenshots back to your repository

Don't forget to add your SCRNIFY_API_KEY to your GitHub repository secrets!

Advanced Techniques

Organizing Screenshots with Metadata

As your documentation grows, you'll want to organize screenshots more systematically. Let's enhance our script to include metadata:

// Add this function to generate a JSON metadata file
async function generateMetadata() {
    const metadata = {
        generated: new Date().toISOString(),
        version: process.env.APP_VERSION || '1.0.0',
        screenshots: screenshotTargets.map(target => ({
            name: target.name,
            url: target.url,
            path: `${target.name}.png`,
            width: target.width,
            height: target.height,
            fullPage: target.fullPage || false,
        })),
    }

    const metadataPath = path.join(SCREENSHOTS_DIR, 'metadata.json')
    await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2))
    console.log(`πŸ“ Metadata saved to ${metadataPath}`)
}

// Call this after all screenshots are captured
async function captureAllScreenshots() {
    console.log(`πŸ“Έ Starting screenshot capture for ${screenshotTargets.length} targets...`)

    const promises = screenshotTargets.map(target => captureScreenshot(target))

    try {
        await Promise.all(promises)
        await generateMetadata() // Generate metadata after all screenshots are captured
        console.log('πŸŽ‰ All screenshots captured successfully!')
    } catch (error) {
        console.error('❌ Error during screenshot capture process:', error.message)
    }
}

This creates a metadata.json file that documents when the screenshots were taken and their properties, which is useful for documentation systems.

Capturing Before/After Screenshots for Feature Comparisons

When documenting new features, it's often helpful to show before/after comparisons:

// Add version parameter to your targets
const screenshotTargets = [
    {
        name: 'dashboard-v1',
        url: 'https://your-app.com/dashboard?version=v1',
        width: 1280,
        height: 800,
        fullPage: false,
    },
    {
        name: 'dashboard-v2',
        url: 'https://your-app.com/dashboard?version=v2',
        width: 1280,
        height: 800,
        fullPage: false,
    },
]

You can then use these screenshots in your documentation to show how features have evolved.

Batch Processing for Large Documentation Sites

For large documentation sites with hundreds of screenshots, you'll want to process them in batches to avoid overwhelming the API:

/**
 * Process screenshots in batches
 * @param {Array} targets - Screenshot targets
 * @param {number} batchSize - Number of screenshots to process at once
 */
async function processBatches(targets, batchSize = 5) {
    console.log(`πŸ“Έ Processing ${targets.length} screenshots in batches of ${batchSize}...`)

    // Split targets into batches
    const batches = []
    for (let i = 0; i < targets.length; i += batchSize) {
        batches.push(targets.slice(i, i + batchSize))
    }

    // Process each batch sequentially
    for (let i = 0; i < batches.length; i++) {
        console.log(`⏱️ Processing batch ${i + 1} of ${batches.length}...`)
        const batch = batches[i]
        const promises = batch.map(target => captureScreenshot(target))

        try {
            await Promise.all(promises)
            console.log(`βœ… Batch ${i + 1} completed successfully`)
        } catch (error) {
            console.error(`❌ Error in batch ${i + 1}:`, error.message)
        }

        // Add a small delay between batches to be nice to the API
        if (i < batches.length - 1) {
            console.log('Waiting before next batch...')
            await new Promise(resolve => setTimeout(resolve, 2000))
        }
    }
}

// Replace captureAllScreenshots with this
async function captureAllScreenshots() {
    try {
        await processBatches(screenshotTargets, 5)
        await generateMetadata()
        console.log('πŸŽ‰ All screenshots captured successfully!')
    } catch (error) {
        console.error('❌ Error during screenshot capture process:', error.message)
    }
}

This approach processes screenshots in smaller batches, which is more reliable for large documentation sets.

Using Caching for Efficiency

Scrnify supports caching screenshots to improve performance and reduce costs. This is especially useful for documentation where the UI doesn't change frequently:

// Add cache_ttl parameter to your requests
params.append('cache_ttl', '86400') // Cache for 24 hours (in seconds)

For signed API keys, you'll need to include the cache_ttl parameter and generate a signature:

const crypto = require('crypto')

function generateSignature(queryString, secret) {
    const hmac = crypto.createHmac('sha256', secret)
    hmac.update(queryString)
    return hmac.digest('hex')
}

// In your captureScreenshot function
const params = new URLSearchParams({
    key: API_KEY,
    url: target.url,
    type: 'image',
    format: 'png',
    width: target.width.toString(),
    waitUntil: 'networkIdle',
    cache_ttl: '86400', // Required for signed API keys
})

// Add other parameters...

// If using signed API keys
if (process.env.SCRNIFY_API_SECRET) {
    const signature = generateSignature(params.toString(), process.env.SCRNIFY_API_SECRET)
    params.append('signature', signature)
}

Conclusion

Automating your documentation screenshots with Scrnify brings numerous benefits:

  1. Time savings: No more manual screenshot capturing and editing
  2. Consistency: All screenshots have the same style and quality
  3. Accuracy: Screenshots always reflect the current state of your application
  4. Workflow integration: Easily incorporate screenshot generation into your CI/CD pipeline
  5. Scalability: Handle hundreds of screenshots without manual effort

By implementing the techniques in this article, you can create a robust system that automatically keeps your documentation screenshots fresh and accurate, freeing you to focus on more important tasksβ€”like building great features for your users!

The next time you're faced with updating dozens of screenshots after a UI change, you'll be glad you set up this automation. Your future self (and your documentation readers) will thank you! πŸ™

Try the SCRNIFY open beta and review current pricing. scrnify.com

Have you implemented automated documentation screenshots in your workflow? We'd love to hear about your experience in the comments below!

Cheers, Laura & Heidi πŸ‡¦πŸ‡Ή

P.S. If you found this article helpful, check out our other guides on using Playwright with Scrnify and capturing website screenshots with JavaScript. Follow us on Twitter @scrnify for more tips and updates!

Open beta

Start with one Capture

Join the open beta and create screenshots or videos without local browser setup.

Join Open Beta