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

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!

Get free access to the SCRNIFY API during our open beta and start generating screenshots today!

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 to get one free during our 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! πŸ™

Get free access to the SCRNIFY API during our open beta and start generating screenshots today! 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!

Ready to Get Started?

Sign up now and start capturing stunning screenshots in minutes.

Sign Up Now