Skip to content

Instantly share code, notes, and snippets.

@DavydeVries
Last active February 16, 2025 11:15
Show Gist options
  • Select an option

  • Save DavydeVries/90a1123824e906771466886f4fd6314c to your computer and use it in GitHub Desktop.

Select an option

Save DavydeVries/90a1123824e906771466886f4fd6314c to your computer and use it in GitHub Desktop.
Serverless screenshot API with Puppeteer and Chromium
// /src/app/api/screenshot/route.ts
import puppeteer from 'puppeteer-core'
import chromium from '@sparticuz/chromium-min'
import { NextRequest } from 'next/server'
export const isPreviewOrProduction =
process.env.VERCEL_ENV === 'preview' ||
process.env.VERCEL_ENV === 'production'
async function getBrowser() {
return puppeteer.launch({
args: [
...chromium.args,
'--hide-scrollbars',
'--disable-web-security',
'--no-sandbox',
'--disable-setuid-sandbox',
],
defaultViewport: chromium.defaultViewport,
executablePath: isPreviewOrProduction
? await chromium.executablePath(
'https://github.com/Sparticuz/chromium/releases/download/v130.0.0/chromium-v130.0.0-pack.tar',
)
:
// To install local Chromium for screenshots run:
// `npx @puppeteer/browsers install chromium@latest --path /tmp/localChromium`
// After that, move the Chromium binary to a persistent location, e.g., `/usr/local/chromium`,
// and update the path accordingly in the script.
'/usr/local/chromium/mac_arm-1390993/chrome-mac/Chromium.app/Contents/MacOS/Chromium',
headless: isPreviewOrProduction
? chromium.headless === 'new'
? true
: (chromium.headless as boolean | 'shell')
: false,
})
}
export async function GET(req: NextRequest) {
const { searchParams } = new URL(req.url)
let url = searchParams.get('url')
if (!url) {
return new Response(JSON.stringify({ error: 'Missing `url` parameter' }), { status: 400 })
}
if (!url.startsWith('http://') && !url.startsWith('https://')) {
url = `https://${url}`
}
console.info(
'Try to screenshot with: ' +
(isPreviewOrProduction
? 'https://github.com/Sparticuz/chromium/releases/download/v130.0.0/chromium-v130.0.0-pack.tar'
: '/usr/local/chromium/mac_arm-1390852/chrome-mac/Chromium.app/Contents/MacOS/Chromium'),
)
let browser
try {
browser = await getBrowser()
const page = await browser.newPage()
await page.setUserAgent(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36',
)
await page.setExtraHTTPHeaders({
AcceptLanguage: 'en-US,en;q=0.9',
Referer: url,
locale: 'en-US',
})
await page.goto(url, {
waitUntil: ['load', 'domcontentloaded', 'networkidle2'],
})
const screenshot = await page.screenshot({ fullPage: true })
await browser.close()
return new Response(screenshot, {
status: 200,
headers: {
'Content-Type': 'image/png',
},
})
} catch (error) {
console.error('Error capturing screenshot:', error)
if (browser) await browser.close()
return new Response(JSON.stringify({ error: 'Failed to capture screenshot' }), { status: 500 })
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment