April 2026 · 8 min read · Blog
Screenshot API for Vue.js — Composables, Nuxt Integration, and Scheduled Captures
A practical guide to integrating SnapAPI into Vue 3 apps and Nuxt projects, with reusable composables, server-side rendering patterns, and automated screenshot pipelines.
Why Vue Developers Need a Screenshot API
Vue 3 and Nuxt have become go-to choices for content-heavy applications: news portals, SaaS dashboards, documentation sites, and e-commerce storefronts. All of these generate a common need — reliable, programmatic screenshots for social sharing previews, PDF exports, visual regression testing, and content thumbnails. Rolling your own Playwright server adds operational complexity that has nothing to do with your core product. SnapAPI is a REST endpoint that handles all of it.
Installation and Quick Start
SnapAPI has no Vue-specific SDK — it's a plain REST API, so you call it with fetch or axios. Install SnapAPI's JavaScript SDK if you want typed helpers:
npm install @snapapi/js
Or call the API directly with native fetch — no dependencies required. Get your free API key at snapapi.pics (200 screenshots/month free, no credit card).
Building a useScreenshot Composable
Composables are Vue 3's answer to reusable stateful logic. A useScreenshot composable wraps the SnapAPI call with loading and error state, making it trivially easy to add screenshot capability to any component:
// composables/useScreenshot.ts
import { ref } from 'vue'
export function useScreenshot() {
const loading = ref(false)
const error = ref<string | null>(null)
const imageUrl = ref<string | null>(null)
async function capture(url: string, options: Record<string, string> = {}) {
loading.value = true
error.value = null
imageUrl.value = null
try {
const params = new URLSearchParams({
url,
format: 'webp',
width: '1280',
height: '800',
access_key: import.meta.env.VITE_SNAP_KEY,
...options
})
const resp = await fetch(`https://api.snapapi.pics/v1/screenshot?${params}`)
if (!resp.ok) throw new Error(`SnapAPI error: ${resp.status}`)
const blob = await resp.blob()
imageUrl.value = URL.createObjectURL(blob)
} catch (e) {
error.value = e instanceof Error ? e.message : 'Unknown error'
} finally {
loading.value = false
}
}
return { loading, error, imageUrl, capture }
}
Use it in any component:
<script setup lang="ts">
import { useScreenshot } from '@/composables/useScreenshot'
const { loading, error, imageUrl, capture } = useScreenshot()
</script>
<template>
<div>
<button @click="capture('https://example.com')" :disabled="loading">
{{ loading ? 'Capturing...' : 'Take Screenshot' }}
</button>
<img v-if="imageUrl" :src="imageUrl" alt="Screenshot" />
<p v-if="error" class="error">{{ error }}</p>
</div>
</template>
Nuxt Server Routes for Safe API Key Handling
In Nuxt, never expose your SnapAPI key to the browser. Use a Nuxt server route (or API route) to proxy the request server-side:
// server/api/screenshot.get.ts
export default defineEventHandler(async (event) => {
const { url, format = 'webp', width = '1280' } = getQuery(event)
if (!url || typeof url !== 'string') {
throw createError({ statusCode: 400, statusMessage: 'url is required' })
}
const params = new URLSearchParams({
url,
format: format as string,
width: width as string,
access_key: process.env.SNAP_KEY!
})
const resp = await fetch(`https://api.snapapi.pics/v1/screenshot?${params}`)
if (!resp.ok) throw createError({ statusCode: resp.status, statusMessage: 'SnapAPI error' })
const contentType = resp.headers.get('content-type') ?? 'image/webp'
setResponseHeader(event, 'content-type', contentType)
setResponseHeader(event, 'cache-control', 'public, max-age=3600')
return sendStream(event, resp.body!)
})
Now your Nuxt frontend calls /api/screenshot?url=... — the API key never leaves your server. The cache-control header means Nuxt's CDN layer (Vercel, Netlify, Cloudflare) will cache repeated screenshot requests automatically.
OG Image Generation for Nuxt Content
Nuxt Content sites need Open Graph images for every post. Hook into the content:file:afterParse hook (Nuxt 3 Content v2) or a build script to generate screenshots of all published posts:
// scripts/generate-og.ts
import { readdir } from 'fs/promises'
const BASE = 'https://your-nuxt-site.com'
const KEY = process.env.SNAP_KEY!
const slugs = (await readdir('content/blog'))
.filter(f => f.endsWith('.md'))
.map(f => f.replace('.md', ''))
for (const slug of slugs) {
const url = `${BASE}/blog/${slug}`
const resp = await fetch(
`https://api.snapapi.pics/v1/screenshot?url=${encodeURIComponent(url)}&format=webp&width=1200&height=630&access_key=${KEY}`
)
const buffer = Buffer.from(await resp.arrayBuffer())
await writeFile(`public/og/${slug}.webp`, buffer)
console.log('Generated OG:', slug)
}
Run this script as a post-build step in your CI pipeline. The generated WebP images are served as static assets from your CDN — zero runtime cost, perfect cache hit rate.
Pinia Store for Screenshot Management
For applications that generate many screenshots (dashboards, report builders, content management tools), a Pinia store keeps captures organised and prevents redundant API calls:
// stores/screenshots.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useScreenshotStore = defineStore('screenshots', () => {
const cache = ref<Map<string, string>>(new Map())
const pending = ref<Set<string>>(new Set())
const isCached = computed(() => (url: string) => cache.value.has(url))
async function getScreenshot(url: string): Promise<string> {
if (cache.value.has(url)) return cache.value.get(url)!
if (pending.value.has(url)) {
// Wait for in-flight request
await new Promise(r => setTimeout(r, 200))
return getScreenshot(url)
}
pending.value.add(url)
const resp = await fetch('/api/screenshot?url=' + encodeURIComponent(url))
const blob = await resp.blob()
const objectUrl = URL.createObjectURL(blob)
cache.value.set(url, objectUrl)
pending.value.delete(url)
return objectUrl
}
return { cache, isCached, getScreenshot }
})
Visual Regression Testing with Playwright + SnapAPI
Vue component tests with Playwright cover interaction logic, but they won't catch CSS regressions in your production build. SnapAPI visual regression tests complement your Playwright suite by screenshotting the fully deployed app — catching CDN caching issues, third-party script failures, and CSS variables that break differently in production vs. the dev server.
Add a regression.test.ts file that runs post-deploy, screenshots key routes, and diffs against approved baselines stored in your repository. Any pixel diff above 2% triggers a Slack alert and a GitHub Actions annotation.
Getting Started
SnapAPI is free for 200 screenshots per month with no credit card required. Sign up at snapapi.pics, get your key, drop it in .env as VITE_SNAP_KEY (client-exposed) or SNAP_KEY (server-only), and your Vue or Nuxt app can start capturing screenshots immediately. SDKs for JavaScript, Python, Go, PHP, Swift, and Kotlin are available on GitHub for teams that prefer typed wrappers over raw fetch calls.