How to Take a Screenshot of a Website with JavaScript
You need a screenshot. You want to do it in JavaScript. No Puppeteer. No browser driver. Just a fetch call.
That's what this tutorial covers.
The Problem: Puppeteer Overhead
Typical Puppeteer approach in Node.js:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
args: ['--no-sandbox']
});
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: 'screenshot.png' });
await browser.close();
})();
Issues:
- 300MB+ dependency
- Browser launch overhead (2-5 seconds)
- Process management complexity
- Fails in serverless (Lambda, Vercel Functions)
- Version conflicts with system Chrome
The Solution: PageBolt API
One fetch call. PNG back.
const response = await fetch('https://api.pagebolt.dev/v1/screenshot', {
method: 'POST',
headers: { 'Authorization': `Bearer ${apiKey}` },
body: JSON.stringify({ url: 'https://example.com' })
});
const buffer = await response.arrayBuffer();
Node.js: Save Screenshot to File
const fs = require('fs');
const path = require('path');
async function takeScreenshot(url, filename = 'screenshot.png') {
const apiKey = process.env.PAGEBOLT_API_KEY;
if (!apiKey) {
throw new Error('PAGEBOLT_API_KEY environment variable not set');
}
const response = await fetch('https://api.pagebolt.dev/v1/screenshot', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: url,
format: 'png',
width: 1280,
height: 720
})
});
if (!response.ok) {
throw new Error(`API error: ${response.status} ${response.statusText}`);
}
const buffer = await response.arrayBuffer();
fs.writeFileSync(filename, Buffer.from(buffer));
console.log(`✅ Screenshot saved: ${filename}`);
console.log(`📊 Size: ${buffer.byteLength} bytes`);
}
// Usage
takeScreenshot('https://example.com', 'homepage.png');
Setup:
# Set your API key
export PAGEBOLT_API_KEY=your-api-key-here
# Run the script
node screenshot.js
Browser: Display Screenshot Inline
In the browser, fetch returns the binary PNG. Convert to base64 to display:
async function takeScreenshot(url) {
const apiKey = 'your-pagebolt-api-key';
const response = await fetch('https://api.pagebolt.dev/v1/screenshot', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: url,
format: 'png',
width: 1280,
height: 720
})
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const buffer = await response.arrayBuffer();
const base64 = btoa(String.fromCharCode(...new Uint8Array(buffer)));
// Display in image tag
const img = document.createElement('img');
img.src = `data:image/png;base64,${base64}`;
document.body.appendChild(img);
}
// Usage
takeScreenshot('https://example.com');
React Component
import { useState } from 'react';
function ScreenshotCapture() {
const [image, setImage] = useState(null);
const [loading, setLoading] = useState(false);
const handleScreenshot = async (e) => {
e.preventDefault();
const url = e.target.url.value;
setLoading(true);
try {
const response = await fetch('https://api.pagebolt.dev/v1/screenshot', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.REACT_APP_PAGEBOLT_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: url,
format: 'png',
width: 1280,
height: 720
})
});
const buffer = await response.arrayBuffer();
const base64 = btoa(String.fromCharCode(...new Uint8Array(buffer)));
setImage(`data:image/png;base64,${base64}`);
} catch (error) {
console.error('Screenshot failed:', error);
} finally {
setLoading(false);
}
};
return (
<div>
<form onSubmit={handleScreenshot}>
<input
type="url"
name="url"
placeholder="https://example.com"
required
/>
<button type="submit" disabled={loading}>
{loading ? 'Taking screenshot...' : 'Take screenshot'}
</button>
</form>
{image && <img src={image} alt="Screenshot" style={{ maxWidth: '100%' }} />}
</div>
);
}
Next.js API Route
// pages/api/screenshot.js
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
const { url } = req.body;
if (!url) {
return res.status(400).json({ error: 'URL required' });
}
try {
const response = await fetch('https://api.pagebolt.dev/v1/screenshot', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PAGEBOLT_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: url,
format: 'png',
width: 1280,
height: 720
})
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const buffer = await response.arrayBuffer();
res.setHeader('Content-Type', 'image/png');
res.send(Buffer.from(buffer));
} catch (error) {
res.status(500).json({ error: error.message });
}
}
Usage:
// In your Next.js client component
const response = await fetch('/api/screenshot', {
method: 'POST',
body: JSON.stringify({ url: 'https://example.com' })
});
const buffer = await response.arrayBuffer();
Batch Screenshots
Screenshot multiple URLs in parallel:
async function batchScreenshots(urls) {
const apiKey = process.env.PAGEBOLT_API_KEY;
const promises = urls.map(url =>
fetch('https://api.pagebolt.dev/v1/screenshot', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: url,
format: 'png',
width: 1280,
height: 720
})
})
.then(r => r.arrayBuffer())
.then(buffer => {
const filename = `screenshots/${new URL(url).hostname}.png`;
fs.writeFileSync(filename, Buffer.from(buffer));
return filename;
})
);
const results = await Promise.all(promises);
console.log(`✅ ${results.length} screenshots saved`);
}
// Usage
batchScreenshots([
'https://example.com',
'https://example.com/about',
'https://example.com/pricing'
]);
Device Emulation
Screenshot on mobile or tablet:
const response = await fetch('https://api.pagebolt.dev/v1/screenshot', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: 'https://example.com',
format: 'png',
viewportDevice: 'iphone_14_pro' // 25+ devices available
})
});
const buffer = await response.arrayBuffer();
Error Handling
async function takeScreenshotSafe(url) {
try {
const response = await fetch('https://api.pagebolt.dev/v1/screenshot', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PAGEBOLT_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ url: url }),
timeout: 30000 // 30 second timeout
});
if (response.status === 401) {
throw new Error('Invalid API key');
}
if (response.status === 429) {
throw new Error('Rate limited. Upgrade your plan or wait.');
}
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const buffer = await response.arrayBuffer();
if (buffer.byteLength === 0) {
throw new Error('Empty response from API');
}
return buffer;
} catch (error) {
console.error(`Screenshot failed: ${error.message}`);
throw error;
}
}
Pricing
Free tier: 100 screenshots/month (covers development)
Starter: $29/month for 5,000 screenshots
Growth: $79/month for 25,000 screenshots
Scale: $199/month for 100,000 screenshots
For a web app that takes 1 screenshot per user session, the free tier covers ~3000 active users/month.
Real-World Use Cases
Website monitoring
Take daily screenshots to detect visual regressions:
const cron = require('node-cron');
cron.schedule('0 9 * * *', async () => {
const screenshot = await takeScreenshot('https://mysite.com');
// Compare with previous day's screenshot
// Alert if significant visual change detected
});
Social media preview generation
Generate preview images for shared links:
// When user shares a link, capture its visual appearance
const preview = await takeScreenshot(url);
// Store and use as og:image
E-commerce product verification
Screenshot products after upload to verify rendering:
async function uploadProduct(productData) {
// Save product to database
const productUrl = `https://mystore.com/product/${productData.id}`;
// Take screenshot to verify it rendered correctly
const screenshot = await takeScreenshot(productUrl);
// Store screenshot for visual QA
await saveProductScreenshot(productData.id, screenshot);
}
Environment Setup
Node.js:
export PAGEBOLT_API_KEY=your-api-key-here
node screenshot.js
React (.env):
REACT_APP_PAGEBOLT_API_KEY=your-api-key-here
Next.js (.env.local):
PAGEBOLT_API_KEY=your-api-key-here
Next Steps
- Get a free PageBolt API key — 100 screenshots/month
- Copy one of the examples above (Node.js, React, Next.js, or browser)
- Set your API key as an environment variable
- Run it
Done. No Puppeteer. No browser driver. Just JavaScript + fetch.
Top comments (0)