Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Deploy Puppeteer as a server-side Node.js Function on Vercel, not in browser-side code. For the deployment pattern described in Vercel’s guide, install puppeteer-core, provide Chromium separately with @sparticuz/chromium-min, expose a route that launches the supplied executable, and then deploy from the project root with vercel --prod. This avoids packaging Puppeteer’s full browser download inside the function bundle.
The exact archive, extraction, memory, and duration settings depend on your project, package versions, and Vercel plan. The working example below is deliberately explicit about those boundaries so you can verify them in your current deployment.
What you are deploying
A Vercel Function receives an HTTP request, starts (or reuses) a headless Chromium process, navigates to a permitted URL, and returns an image or PDF. Vercel supports JavaScript and TypeScript Functions on the Node.js runtime; Node.js is the default when no additional runtime is configured.
Free tools Windows power users keep installed
One-click scans. No signup required.
Keep the browser launch on the server. A browser-side bundle cannot safely contain your Chromium executable or secret configuration, and it would expose automation work to every visitor.
#1 Best Overall
Prerequisites and project layout
- A Vercel account and a Node.js project managed from its root directory.
- A current Node.js version supported by your selected Vercel runtime and by the Puppeteer packages you install.
- A route that validates input and limits the destinations it will open. Never turn an unauthenticated screenshot endpoint into an unrestricted proxy.
- A plan and project configuration that leave enough function duration and memory for Chromium startup, navigation, and rendering. Vercel’s limits vary by plan and configuration, so check the current limits before choosing production values.
For a Next.js project, a simple App Router layout is:
app/api/screenshot/route.js
package.json
You can use an equivalent JavaScript or TypeScript Vercel Function in another supported framework.
Choose the browser packages
Why puppeteer-core
Vercel’s Puppeteer guide identifies the function bundle size as the constraint and recommends puppeteer-core with a separately supplied Chromium package. The regular puppeteer package downloads its own browser and is too large for the guide’s stated bundle constraint.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Install dependencies
From the project root, install the packages used by the deployment pattern:
npm install puppeteer-core @sparticuz/chromium-min
Pin compatible versions in your lockfile. Puppeteer, the Chromium binary, and the Node.js runtime must agree; upgrading one without checking the others is a common cause of launch errors.
Rank #2
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
The 250 MB figure
Vercel’s Puppeteer guide (listed as updated November 10, 2025) describes a 250 MB function bundle limit. Treat that as a dated guide value, not a permanent platform promise. Confirm the live limit and your project’s generated bundle before deployment.
Provide Chromium at runtime
The accompanying Vercel template uses a build/runtime split: Chromium assets are made available as an archive, the deployed function downloads and extracts them when needed, and the resulting executable path is cached in memory for later invocations on the same warm instance. This is one documented template architecture, not a requirement that every Vercel project use the identical archive host or extraction code.
Whichever provisioning method you select, make these properties explicit:
- The archive is reachable from the deployed function and contains the binary expected by your Chromium package.
- Extraction occurs in a writable temporary directory available to the function.
- The executable path is cached after the first successful extraction, but your code can repeat provisioning after a cold start.
- Package and browser versions are tested together; a successful local install does not prove the deployed binary is compatible.
The template’s exact download and extraction implementation can change. Copy its current implementation when you create your project, then keep the route logic below separate so it is easy to update the provisioning layer.
Build a screenshot route
The following route demonstrates the important controls: server-side launch, a bounded navigation timeout, a controlled viewport, cleanup in a finally block, and a simple URL allow-list. Adapt the Chromium provisioning call to the current @sparticuz/chromium-min instructions and the archive arrangement you selected.
Rank #3
import puppeteer from 'puppeteer-core';
import chromium from '@sparticuz/chromium-min';
let executablePath;
async function getExecutablePath() {
if (!executablePath) {
// Use the archive download/extraction procedure from the current
// Vercel template or @sparticuz/chromium-min documentation here.
executablePath = await chromium.executablePath();
}
return executablePath;
}
function isAllowedTarget(value) {
try {
const u = new URL(value);
return u.protocol === 'https:' && u.hostname === 'example.com';
} catch {
return false;
}
}
export async function GET(request) {
const target = new URL(request.url).searchParams.get('url');
if (!target || !isAllowedTarget(target)) {
return Response.json({ error: 'Only approved HTTPS targets are allowed' }, { status: 400 });
}
let browser;
try {
browser = await puppeteer.launch({
executablePath: await getExecutablePath(),
args: chromium.args,
headless: true,
});
const page = await browser.newPage();
await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });
await page.goto(target, { waitUntil: 'networkidle2', timeout: 30000 });
const image = await page.screenshot({ fullPage: true, type: 'png' });
return new Response(image, {
headers: { 'Content-Type': 'image/png', 'Cache-Control': 'no-store' },
});
} catch (error) {
console.error('Screenshot failed', error);
return Response.json({ error: 'Browser capture failed' }, { status: 502 });
} finally {
if (browser) await browser.close();
}
}
Replace example.com with domains you control or explicitly trust. If callers supply arbitrary URLs, add authentication, hostname and protocol checks, redirect validation, rate limits, and protection against private-network access.
PDF output
For a PDF endpoint, wait for the page to finish loading, then call page.pdf() and return application/pdf:
const pdf = await page.pdf({
format: 'A4',
printBackground: true,
margin: { top: '16mm', right: '16mm', bottom: '16mm', left: '16mm' },
});
return new Response(pdf, { headers: { 'Content-Type': 'application/pdf' } });
Use a separate route or a validated format parameter so image and PDF responses cannot be confused by clients or caches.
Local development versus Vercel
The standard puppeteer package is convenient locally because it supplies a browser automatically. The deployed function described by Vercel instead uses puppeteer-core and an external Chromium binary to keep the function small. Do not assume that a locally downloaded browser is the same binary your deployment will execute.
Run the project locally with your framework’s development command, call the route with an approved URL, and inspect the returned image or PDF. Keep local-only launch settings out of production; in particular, do not ship a machine-specific executable path.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Rank #4
- Brand: Wiley
- Set of 2 Volumes
- A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
Deploy and verify
- Commit
package.jsonand the lockfile, along with any archive or extraction assets required by your chosen provisioning method. - From the project root, run
vercel --prodto create a production deployment. - Open the deployed route with a controlled target and record the response status, content type, and capture time.
- Inspect the specific deployment’s function logs if Chromium fails to launch, navigation fails, or the response is truncated.
- Confirm that the intended project, branch, and production deployment were selected; a successful CLI command does not mean the request reached the code you just changed.
Do not describe a deployment as verified until the route has actually been exercised in the deployed environment. Local success alone cannot reveal missing archives, bundle omissions, or runtime permissions.
Duration, memory, and cold starts
Vercel function duration defaults depend on plan and configuration and can be configured only up to the applicable plan limit. Review the current limits rather than copying a single timeout number into production documentation. Browser startup, archive retrieval, extraction, page navigation, JavaScript execution, full-page layout, and PDF generation all consume that budget.
Reduce work before increasing limits
- Use a realistic navigation timeout and fail clearly when it is exceeded.
- Wait for the condition you need instead of always waiting for an unnecessarily long network-idle period.
- Block or avoid third-party resources when they are irrelevant to the output.
- Set a fixed viewport and avoid loading multiple pages in one invocation unless the function is designed and sized for it.
- Close every browser in
finally; leaked processes make warm instances less reliable.
Understand warm-instance caching
Caching the extracted executable path in module memory removes repeated extraction on a warm instance. It is not durable storage: a cold start, redeployment, or instance replacement can run provisioning again. Design the first request to tolerate that extra work.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Common failures and fixes
“Failed to launch the browser”
Usually the executable is absent, not executable, or incompatible with the Puppeteer protocol. Confirm that the archive reached the function, extraction completed in a writable location, and the package versions match the binary. Log the resolved executable path without exposing secrets.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Bundle-size or deployment rejection
Inspect the generated function bundle and dependency tree. Remove the full puppeteer package from the deployed dependency graph, use puppeteer-core, and follow the current lightweight Chromium approach. Recheck the live Vercel size constraint because platform limits can change.
Best Value
Navigation timeout
The target may be slow, waiting on an event that never occurs, or blocked by a bot challenge. Test a controlled page, set an explicit timeout, and inspect logs for the failing phase. Then review the duration available under your plan instead of raising the timeout blindly.
Blank or partial output
Wait for the selector or application state that proves the content is rendered, not merely for the initial response. Lazy-loaded pages may require scrolling or an application-specific readiness check. A full-page screenshot also increases layout and memory work.
The deployment appears unchanged
Inspect the deployment details and logs, verify the production branch, and confirm that the request uses the new deployment URL. A different project or preview URL can make a correct fix look ineffective.
Security and reliability checklist
- Allow only HTTPS destinations and approved hostnames.
- Validate redirects as well as the initial URL so a trusted hostname cannot redirect into a private address.
- Authenticate capture requests and apply rate limits.
- Do not return raw browser error details to untrusted callers.
- Set response cache headers intentionally; screenshots of private pages should not be publicly cached.
- Track cold-start and navigation failures separately so archive problems are not mistaken for target-site failures.
Or skip the browser setup
ScreenshotNeo is a hosted website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF without you packaging Chromium in a Vercel Function. It removes cookie and consent banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status.
Use the API directly from your server or automation job:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
See the ScreenshotNeo documentation for request options. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Every plan includes its features; 1,000 screenshots per month are free with no card, and paid plans start at $5 for 3,000. Sign up for the free plan.
Frequently Asked Questions
Can I run Puppeteer in a Vercel Edge Function?
This deployment pattern targets Vercel’s Node.js runtime. Use a Node.js Function for Chromium work rather than assuming Edge runtime compatibility.
Recommended Free Tools
Should I deploy the full Puppeteer package?
For the Vercel guide’s bundle-constrained pattern, no: use puppeteer-core and provide Chromium separately. The full package remains useful for local development when its bundled browser is convenient.
Does executable-path caching survive every request?
It can be reused by later requests on the same warm instance, but cold starts and instance replacement run provisioning again.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

