How to Generate a Website Thumbnail with Puppeteer for an Indian Blog
Use Puppeteer to capture a polished website thumbnail, choose the right viewport and format, and prepare a large image for Google Discover.
Use Puppeteer to open the page at a deliberate desktop viewport and save the rendered frame with page.screenshot(). For a standard 16:9 thumbnail, a 1200 × 675 viewport gives you an image at the width Google recommends for larger Discover previews. Set the viewport before navigation, wait for the content your target page needs, and inspect the result at the size it will appear on your blog. This workflow applies to Indian blogs just as it does to other sites; the research does not identify special Puppeteer rules for India.
A screenshot creates the image file. It does not tell Google which image to use: add a representative og:image or schema.org image signal separately if you want to indicate a preferred preview.
1. Install Puppeteer and generate a thumbnail
Puppeteer’s Page.screenshot() captures the rendered page. This example uses ES modules and writes a PNG to disk.
npm init -y
npm install puppeteer
Save the following as thumbnail.mjs and run it with node thumbnail.mjs:
import puppeteer from 'puppeteer';
const url = process.argv[2] ?? 'https://example.com';
const output = process.argv[3] ?? 'website-thumbnail.png';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.setViewport({
width: 1200,
height: 675,
deviceScaleFactor: 1,
});
await page.goto(url, {
waitUntil: 'networkidle2',
timeout: 60_000,
});
await page.screenshot({
path: output,
type: 'png',
});
console.log(`Saved ${output}`);
} finally {
await browser.close();
}
For example: node thumbnail.mjs https://your-blog.example/articles/post website-thumbnail.png. Puppeteer’s screenshot workflow and options are documented in its screenshot guide; its viewport documentation recommends setting dimensions before navigation because some sites respond to viewport changes.
2. Choose viewport, crop, and image format
The image dimensions and framing depend on where the thumbnail will appear. Pick the capture mode deliberately rather than assuming a full-page capture is a good thumbnail.
| Capture choice | Use it when | What to consider |
|---|---|---|
| Fixed viewport | You want a predictable frame, such as a 16:9 blog card. | Set width and height before navigation. A 1200 × 675 viewport is 16:9. |
clip |
You need a specific rectangle from the page. | Set the clip’s x, y, width, and height; keep the important content inside it. |
fullPage: true |
You need the whole page for review or documentation. | A long page can produce an unwieldy, very tall image that does not work as a compact thumbnail. |
Puppeteer supports screenshot paths, fullPage, clip, image type, and quality options. For JPEG, specify type: 'jpeg' and an integer quality from 0 to 100. PNG is lossless and has no quality setting. WebP is available where supported by the installed Puppeteer/browser version; check the current API documentation if you choose it. For a simple thumbnail, PNG is a reliable starting point; choose a compressed format based on your publishing pipeline and verify the saved file.
// JPEG example
await page.screenshot({ path: 'thumbnail.jpg', type: 'jpeg', quality: 82 });
// Clip example
await page.screenshot({
path: 'thumbnail-crop.png',
clip: { x: 0, y: 0, width: 1200, height: 675 },
});
// Full-page example
await page.screenshot({ path: 'whole-page.png', fullPage: true });
To create a higher-density capture, increase deviceScaleFactor, for example to 2. The resulting bitmap has more pixels per CSS pixel and a larger file. Choose dimensions with the intended display size and crop in mind; a high-density capture does not fix poor composition.
3. Wait for the right page state
waitUntil: 'networkidle2' is used in Puppeteer’s screenshot example, but it is not correct for every site. Some pages keep analytics, chat, or other connections open; other pages render important content after the navigation event. If the screenshot is missing content, use a wait condition that matches the site rather than adding an arbitrary long delay.
// For a page whose main content appears after navigation:
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60_000 });
await page.waitForSelector('main article', { timeout: 15_000 });
// For a page with a known client-side delay:
await page.waitForTimeout(1_500);
Use a selector that is stable for the site, such as the article container, and keep a timeout so a missing element does not stall a batch indefinitely. A fixed delay is a fallback for known behavior, not a universal readiness test. If fonts or images arrive late, wait for the relevant asset or inspect the page before capturing.
4. Google Discover image considerations
If Discover previews are a goal, Google Search Central recommends an image at least 1200 pixels wide, more than 300,000 total pixels, and a 16:9 aspect ratio for larger image previews. A 1200 × 675 image is 810,000 pixels and meets those size recommendations. Google recommends enabling large previews with max-image-preview:large or using AMP. These are recommendations, not a promise that Discover will show or distribute the page.
Google may crop the image automatically, so keep the key visual content away from edges. Use a relevant image that represents the page, not a generic logo or a text-heavy graphic. Google says “No special tags or structured data are required” for basic Discover eligibility; separately, og:image or schema.org markup can signal a preferred image and influence image selection, but cannot force a particular display. See Google’s Discover guidance and visual elements gallery.
<meta name="robots" content="max-image-preview:large">
<meta property="og:image" content="https://your-blog.example/images/article-thumbnail.jpg">
Publish the image at a stable, crawlable URL and use the same representative image in the page’s metadata. The screenshot file and the metadata are separate parts of the publishing workflow.
5. Run Puppeteer in a recurring job
For one-off captures, run the script locally. For recurring thumbnail generation, run it in the same environment as the site’s publishing workflow and make the output path explicit. Each capture launches or uses a browser process and loads the target page, so batch work consumes CPU, memory, network, and browser time. Reuse a browser for a small batch rather than launching one browser per URL, and close pages after each capture.
const urls = [
'https://example.com/post-one',
'https://example.com/post-two',
];
const browser = await puppeteer.launch({ headless: true });
try {
for (const [index, url] of urls.entries()) {
const page = await browser.newPage();
try {
await page.setViewport({ width: 1200, height: 675, deviceScaleFactor: 1 });
await page.goto(url, { waitUntil: 'networkidle2', timeout: 60_000 });
await page.screenshot({ path: `thumbnail-${index + 1}.png` });
} finally {
await page.close();
}
}
} finally {
await browser.close();
}
For reliability, handle navigation and selector timeouts per URL so one failed page does not silently produce a misleading image. Log the URL and error, retry only transient failures with a limit, and verify that the output file exists and is non-empty before publishing it. The research sources do not provide a universal resource estimate or benchmark; measure the workflow with your pages and runtime.
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Browser fails to launch | The runtime cannot find or start Chromium, or lacks required system libraries. | Check the Puppeteer installation and runtime setup; use the official troubleshooting guide for platform-specific dependencies. |
| Screenshot is blank or incomplete | The page has not rendered its main content, or navigation completed before client-side work. | Wait for a meaningful selector or required asset, then capture. Confirm the selector exists on that page. |
| Navigation times out | The page is slow or never reaches the chosen network-idle state. | Try domcontentloaded and wait for the actual content selector; retain a finite timeout. |
| Thumbnail is unexpectedly tall | fullPage: true captured the entire document. |
Remove that option and use a fixed viewport or a deliberate clip. |
| Important content is cut off | The viewport or clip does not match the page layout, or a responsive layout changed. | Set the viewport before navigation, inspect the capture, and adjust its dimensions or clip. |
| Output format or quality is wrong | The extension and screenshot type do not match, or quality was set for PNG. |
Match path extension to type; set quality only for JPEG and check the saved image. |
7. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server from ScreenshotNeo. One GET request returns an image or PDF, and its documentation lists the available capture options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://your-blog.example/article -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://your-blog.example/article"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://your-blog.example/article' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
It removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are never billed, with response headers identifying the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Create a free ScreenshotNeo account to try 1,000 screenshots a month with no card.
FAQ
How do I save a Puppeteer screenshot as an image?
Pass a file path to page.screenshot(). Set type when you need a format other than the default PNG, and match the filename extension to that type.
Should I use a screenshot of the whole website as the thumbnail?
Usually a fixed viewport or crop gives a more controlled image. Use a full-page image when the long-page view itself is useful and fits the destination.
Does a 1200-pixel image guarantee a Google Discover preview?
No. It meets part of Google’s large-image recommendation; eligibility and display are not guaranteed.
Are there special screenshot requirements for an Indian blog?
The Puppeteer capture workflow and cited Discover image recommendations are general. The research does not establish India-specific capture requirements.


