REACT

Screenshot API for React Applications

Capture screenshots of your React app pages, generate Open Graph images, run visual regression on every PR, and automate React component thumbnails — all via REST API.

Start Free — 200 Screenshots

Why React Teams Use Screenshot APIs

React applications have unique screenshot needs. Server-rendered Next.js pages need OG image generation. Single-page apps need screenshots after hydration and data loading. Storybook component libraries benefit from automated component thumbnails. CI/CD pipelines need visual regression on every PR before merging to main.

SnapAPI handles all of these use cases through a single REST API. No Puppeteer configuration in your Next.js API routes, no Playwright servers to manage in your GitHub Actions, no Chromium binary bloating your serverless functions. One HTTPS call returns a screenshot.

Next.js OG Image Generation with SnapAPI

Every Next.js blog post and marketing page benefits from custom Open Graph images. Here is a Next.js API route that generates OG screenshots on demand using SnapAPI:

// app/api/og/route.ts
import { NextRequest, NextResponse } from 'next/server';

const SNAP_KEY = process.env.SNAPAPI_KEY!;

export async function GET(req: NextRequest) {
  const { searchParams } = new URL(req.url);
  const targetUrl = searchParams.get('url');

  if (!targetUrl) {
    return NextResponse.json({ error: 'url required' }, { status: 400 });
  }

  const params = new URLSearchParams({
    access_key: SNAP_KEY,
    url: targetUrl,
    width: '1200',
    height: '630',
    format: 'jpeg',
    quality: '90',
    full_page: 'false',
    delay: '500',
  });

  const res = await fetch(
    `https://api.snapapi.pics/screenshot?${params}`,
    { next: { revalidate: 3600 } }  // Cache 1hr
  );

  if (!res.ok) {
    return NextResponse.json({ error: 'Screenshot failed' }, { status: 500 });
  }

  const img = await res.arrayBuffer();
  return new NextResponse(img, {
    headers: {
      'Content-Type': 'image/jpeg',
      'Cache-Control': 'public, max-age=3600, s-maxage=86400',
    },
  });
}

React Hook for Screenshot Capture

// hooks/useScreenshot.ts
import { useState, useCallback } from 'react';

interface ScreenshotOptions {
  width?: number;
  height?: number;
  fullPage?: boolean;
  format?: 'png' | 'jpeg';
  delay?: number;
}

export function useScreenshot() {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const capture = useCallback(async (url: string, options: ScreenshotOptions = {}) => {
    setLoading(true);
    setError(null);
    try {
      // Call your Next.js API route (keeps API key server-side)
      const params = new URLSearchParams({
        url,
        width: String(options.width ?? 1280),
        height: String(options.height ?? 800),
        fullPage: String(options.fullPage ?? false),
        format: options.format ?? 'png',
        delay: String(options.delay ?? 0),
      });
      const res = await fetch(`/api/screenshot?${params}`);
      if (!res.ok) throw new Error(await res.text());
      const blob = await res.blob();
      return URL.createObjectURL(blob);
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Unknown error');
      return null;
    } finally {
      setLoading(false);
    }
  }, []);

  return { capture, loading, error };
}

// Usage in a component:
// const { capture, loading } = useScreenshot();
// const url = await capture('https://example.com', { width: 1440, fullPage: true });

Visual Regression Testing for React Components

Add visual regression to your React CI pipeline. This GitHub Actions workflow captures screenshots of your Storybook stories on every PR and compares them to baseline snapshots stored as artifacts:

# .github/workflows/visual-regression.yml
name: Visual Regression
on:
  pull_request:
    paths: ['src/**', 'stories/**']

jobs:
  visual-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm ci && npm run build-storybook

      - name: Start Storybook server
        run: npx http-server storybook-static -p 6006 &

      - name: Capture story screenshots
        env:
          SNAPAPI_KEY: ${{ secrets.SNAPAPI_KEY }}
        run: node scripts/capture-stories.mjs

      - name: Upload screenshots
        uses: actions/upload-artifact@v4
        with:
          name: story-screenshots-${{ github.sha }}
          path: screenshots/
// scripts/capture-stories.mjs
const KEY = process.env.SNAPAPI_KEY;
const BASE = 'http://localhost:6006';

const stories = [
  '?path=/story/button--primary',
  '?path=/story/button--secondary',
  '?path=/story/card--default',
  '?path=/story/modal--open',
  '?path=/story/form--login',
];

for (const story of stories) {
  const url = encodeURIComponent(BASE + story);
  const r = await fetch(
    `https://api.snapapi.pics/screenshot?access_key=${KEY}&url=${url}&width=800&height=600&delay=1000&format=png`
  );
  const slug = story.replace(/[^a-z0-9]/gi, '_');
  await Bun.write(`screenshots/${slug}.png`, r.body);
  console.log('captured:', story);
}

Pricing

Free tier includes 200 screenshots/month — enough to ship your first React screenshot feature. Paid plans: $19/month for 5,000 screenshots, $79/month for 50,000. No infrastructure to manage, works from Vercel, Netlify, AWS Lambda, or any Node.js server.

Get your API key at snapapi.pics/dashboard — takes 60 seconds.

Server Components and Screenshot APIs: The Perfect Match

React Server Components and Next.js App Router have changed how React apps generate content. Screenshot APIs pair beautifully with this model. Server Components can call SnapAPI directly — no client-side state, no useEffect, no loading spinners. The screenshot is fetched server-side and streamed to the client as an image tag.

// app/components/SitePreview.tsx (Server Component)
async function SitePreview({ url }: { url: string }) {
  const params = new URLSearchParams({
    access_key: process.env.SNAPAPI_KEY!,
    url,
    width: '1280',
    height: '800',
    format: 'jpeg',
    quality: '85',
  });

  const res = await fetch(
    `https://api.snapapi.pics/screenshot?${params}`,
    { next: { revalidate: 3600 } }
  );

  if (!res.ok) return 
Preview unavailable
; const buf = await res.arrayBuffer(); const base64 = Buffer.from(buf).toString('base64'); return ( <img src={`data:image/jpeg;base64,${base64}`} alt={`Preview of ${url}`} width={1280} height={800} style={{ width: '100%', height: 'auto', borderRadius: 8 }} /> ); } // Usage in any Server Component page: // <SitePreview url="https://customer-site.com" />

Generating Social Cards for React Blog Posts

Every blog post in your React application deserves a tailored social card. Instead of using a static OG image or relying on a third-party social card service, generate screenshot-based OG images that capture your actual post layout — hero image, title, author, and date all visible exactly as the reader sees them.

// lib/og.ts — called at build time or on-demand
export async function generateOgImage(slug: string): Promise<Buffer> {
  const pageUrl = `${process.env.NEXT_PUBLIC_SITE_URL}/blog/${slug}?og=true`;
  const params = new URLSearchParams({
    access_key: process.env.SNAPAPI_KEY!,
    url: pageUrl,
    width: '1200',
    height: '630',
    format: 'jpeg',
    quality: '90',
    delay: '800',
    full_page: 'false',
  });
  const res = await fetch(`https://api.snapapi.pics/screenshot?${params}`);
  if (!res.ok) throw new Error(`OG screenshot failed: ${res.status}`);
  return Buffer.from(await res.arrayBuffer());
}

// In your blog post layout, add ?og=true styling:
// export default function BlogLayout({ params, searchParams }) {
//   const isOg = searchParams.og === 'true';
//   return <div className={isOg ? 'og-mode' : ''}>{children}</div>;
// }

React Portfolio Sites: Auto-Generating Project Thumbnails

Developer portfolio sites built with React often display project cards with thumbnail screenshots of live projects. SnapAPI makes it trivial to keep these thumbnails fresh. Run a build-time script that captures screenshots of each project URL and saves them to your public/thumbnails directory — then import them as static assets.

// scripts/capture-thumbnails.mjs
// Run with: node scripts/capture-thumbnails.mjs
import { writeFile, mkdir } from 'fs/promises';
import path from 'path';

const KEY = process.env.SNAPAPI_KEY;
const OUT = 'public/thumbnails';
await mkdir(OUT, { recursive: true });

const projects = [
  { slug: 'project-a', url: 'https://project-a.example.com' },
  { slug: 'project-b', url: 'https://project-b.example.com' },
  { slug: 'project-c', url: 'https://project-c.example.com' },
];

for (const { slug, url } of projects) {
  const params = new URLSearchParams({
    access_key: KEY, url,
    width: '1280', height: '720',
    format: 'jpeg', quality: '80',
    full_page: 'false',
  });
  const res = await fetch(`https://api.snapapi.pics/screenshot?${params}`);
  if (res.ok) {
    await writeFile(
      path.join(OUT, `${slug}.jpg`),
      Buffer.from(await res.arrayBuffer())
    );
    console.log('captured:', slug);
  }
}

Error Boundaries and Fallbacks for Screenshot Components

Screenshot generation can fail — the target URL may be down, the API may be temporarily unavailable, or the page may time out loading. Always wrap screenshot components in React error boundaries with meaningful fallbacks.

'use client';
import { Component } from 'react';

class ScreenshotErrorBoundary extends Component {
  state = { error: null };

  static getDerivedStateFromError(error) {
    return { error };
  }

  render() {
    if (this.state.error) {
      return (
        <div style={{
          width: this.props.width ?? 1280,
          height: this.props.height ?? 200,
          background: '#f1f5f9',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          borderRadius: 8,
          color: '#64748b',
          fontSize: 14,
        }}>
          Preview unavailable
        </div>
      );
    }
    return this.props.children;
  }
}

// Usage:
// <ScreenshotErrorBoundary width={1280} height={800}>
//   <SitePreview url={url} />
// </ScreenshotErrorBoundary>

Pricing for React Applications

SnapAPI pricing scales from free to enterprise. The free tier (200 screenshots/month) covers development and early testing. The $19/month plan (5,000 screenshots) handles most production React apps — a portfolio site refreshing thumbnails daily, a SaaS dashboard generating OG images per post, or a CI pipeline running visual regression on PRs. The $79/month plan (50,000 screenshots) covers high-volume applications. Sign up at snapapi.pics/dashboard.

React Component Screenshot Testing with Storybook

Storybook is the de facto standard for developing and documenting React components in isolation. Every story you write is a perfect candidate for automated screenshot capture. SnapAPI can screenshot any publicly accessible Storybook deployment, turning your component library into a visual regression test suite with no additional test framework required.

The workflow: build Storybook, deploy to a preview URL (or serve locally in CI), iterate through story IDs, capture one screenshot per story, compare to baseline. When a component changes visually, the diff highlights exactly which story changed and how — far more useful than a failing Playwright snapshot test with no context.

// scripts/storybook-visual-test.mjs
import { readFileSync, writeFileSync, mkdirSync } from 'fs';
import path from 'path';

const KEY = process.env.SNAPAPI_KEY;
const SB_URL = process.env.STORYBOOK_URL ?? 'http://localhost:6006';
const OUT = 'visual-test-results';

// Load stories index from Storybook
const indexRes = await fetch(`${SB_URL}/stories.json`);
const { stories } = await indexRes.json();

mkdirSync(OUT, { recursive: true });
const results = [];

for (const [id, story] of Object.entries(stories)) {
  const storyUrl = `${SB_URL}/iframe.html?id=${id}&viewMode=story`;
  const params = new URLSearchParams({
    access_key: KEY,
    url: storyUrl,
    width: story.parameters?.viewport?.defaultViewport === 'mobile' ? '375' : '800',
    height: '600',
    format: 'png',
    delay: '1500',
    full_page: 'false',
  });

  const res = await fetch(`https://api.snapapi.pics/screenshot?${params}`);
  const slug = id.replace(/[^a-z0-9]/gi, '_');
  const file = path.join(OUT, `${slug}.png`);

  if (res.ok) {
    writeFileSync(file, Buffer.from(await res.arrayBuffer()));
    results.push({ id, name: story.name, file, ok: true });
    console.log('OK:', story.name);
  } else {
    results.push({ id, name: story.name, ok: false });
    console.error('FAIL:', story.name, res.status);
  }
}

writeFileSync(path.join(OUT, 'results.json'), JSON.stringify(results, null, 2));
console.log(`
Captured ${results.filter(r => r.ok).length}/${results.length} stories`);

React App Monitoring: Screenshot Your Production Dashboard

Production monitoring for React applications typically means error tracking and performance metrics. But visual monitoring — regularly screenshotting your live app and checking for unexpected changes — catches a class of bugs that metrics miss: CSS regressions from CDN issues, third-party widget layout breaks, A/B test leakage, and deployment rollbacks that partially applied.

A simple cron job that screenshots your five most critical pages every 30 minutes and alerts on significant pixel change gives you a visual canary for production. Store the screenshots in S3 with lifecycle rules to keep the last 7 days — cheap insurance against visual incidents.

Pricing Summary

SnapAPI is priced for developer teams. Free: 200 screenshots/month, no credit card. Growth: $19/month for 5,000 screenshots — covers most React CI pipelines and OG image workflows. Scale: $79/month for 50,000 — for Storybook libraries with hundreds of components or high-frequency visual monitoring. Get started at snapapi.pics/dashboard.