Replace your Puppeteer screenshot code with a single fetch call. Same pixel-perfect rendering, zero binary dependencies, deploys anywhere Node.js runs.
Your existing HTML templates stay unchanged. Only the render function changes.
import puppeteer from 'puppeteer'
async function renderToImage(
html: string
): Promise<Buffer> {
// Launch a new browser every call
const browser = await puppeteer.launch({
args: ['--no-sandbox',
'--disable-dev-shm-usage']
})
const page = await browser.newPage()
await page.setViewport({
width: 1200, height: 630
})
await page.setContent(html, {
waitUntil: 'networkidle0'
})
const buf = await page.screenshot({
type: 'png'
})
await browser.close()
return buf as Buffer
}
// ~300 MB Chromium binary in bundle
// 2-4 s cold start on Lambda
// Crashes on Vercel (250 MB limit)
async function renderToImage(
html: string
): Promise<Buffer> {
const res = await fetch(
'https://api.renderpix.dev/v1/render',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization':
`Bearer ${process.env.RENDERPIX_KEY}`,
},
body: JSON.stringify({
html, width: 1200, height: 630
}),
}
)
const { url } = await res.json()
const img = await fetch(url)
return Buffer.from(
await img.arrayBuffer()
)
}
// 0 MB added to bundle
// ~200 ms response time
// Deploys on Vercel, Lambda, anywhere
All four options use real Chromium rendering — the difference is who manages the browser.
| Tool | Bundle size | Serverless deploy | Cold start | Maintenance | TypeScript |
|---|---|---|---|---|---|
| RenderPix | 0 MB | Yes | ~200 ms | None | Yes |
| Puppeteer | ~300 MB | No | 2–4 s | High | Yes |
| Playwright | ~300 MB | No | 2–4 s | Medium | Yes |
| Browserless.io | 0 MB | Yes | ~800 ms | Low | Yes |
For most codebases, under 5 minutes. You replace one function — the part that launches a browser and takes a screenshot. Your HTML templates, image sizes, and everything downstream stay exactly the same. The before/after code above is the complete change for the common case.
Yes. RenderPix uses the same headless Chromium engine as Puppeteer — the rendering is identical. Custom fonts, CSS gradients, grid layouts, clip-path, backdrop-filter — anything Chrome renders, we capture. The PNG output is pixel-for-pixel the same as a local Puppeteer screenshot at the same viewport size.
The API is a plain HTTP endpoint, so it works with TypeScript out of the box — no additional types needed. The function in the "After" example above is fully typed. If you want request/response types for autocomplete, they're documented in the API docs.
waitUntil: 'networkidle0'?RenderPix handles this automatically. The renderer waits for fonts, images, and stylesheets to finish loading before capturing. You don't need to configure a wait condition — just send the HTML and the returned image will be fully rendered.
Absolutely. If you use Puppeteer for form filling, scraping, or other browser automation, there's no reason to change that. Just replace the screenshot-specific code paths with RenderPix API calls. The two can coexist in the same project.
Same rendering quality. No binary, no cold starts. Free tier, no credit card required.