ScreenshotNeo

BlogHow-to

Generate Hindi Coaching Class Landing Page Previews with Node.js Playwright

Build repeatable Hindi coaching class landing page previews with Node.js Playwright, full-page capture, locale settings, and reliable Devanagari rendering.

By the ScreenshotNeo team4 October 20268 min read

Use Playwright’s Node.js library to open your coaching class landing page and save a screenshot. For the whole page, call page.screenshot({ path: 'preview.png', fullPage: true }). Set the viewport and browser locale explicitly, install the browser binary that matches your Playwright version, and verify Devanagari fonts in the environment that actually creates the preview.

This guide covers a local capture script, full-page and mobile previews, Hindi text rendering, repeatable output, troubleshooting, and a managed screenshot API option.

1. Set up Node.js and Playwright

Start in an existing Node.js project, or create one and install Playwright. The package version determines which browser binaries it expects, so install the browsers after installing the package and repeat that setup when you upgrade Playwright. See the Playwright browser installation guide.

mkdir hindi-page-preview
cd hindi-page-preview
npm init -y
npm install playwright
npx playwright install chromium

On a Linux CI host, use Playwright’s documented dependency installation command if system libraries required by Chromium are missing:

npx playwright install --with-deps chromium

Use a Node.js version supported by the Playwright release you install. Keep the package version pinned in your project lockfile so local and CI environments resolve the same dependency.

2. Create a full-page Hindi landing page preview

Save this as preview.mjs. Replace the example URL with a page you are authorized to capture. It writes preview.png in the current directory and closes Chromium even if navigation or capture fails.

import { chromium } from 'playwright';

const targetUrl = process.argv[2] ?? 'https://example.com/hindi-coaching';
const browser = await chromium.launch({ headless: true });

try {
  const context = await browser.newContext({
    viewport: { width: 1440, height: 1000 },
    locale: 'hi-IN',
    deviceScaleFactor: 1
  });
  const page = await context.newPage();

  const response = await page.goto(targetUrl, {
    waitUntil: 'networkidle',
    timeout: 45000
  });

  if (!response) {
    throw new Error('Navigation returned no HTTP response.');
  }
  if (!response.ok()) {
    throw new Error(`Page returned HTTP ${response.status()}: ${targetUrl}`);
  }

  await page.screenshot({
    path: 'preview.png',
    fullPage: true,
    animations: 'disabled'
  });

  console.log(`Saved preview.png (${response.status()})`);
  await context.close();
} finally {
  await browser.close();
}

Run it with:

node preview.mjs https://your-site.example/hindi-coaching

The example uses networkidle to wait for network activity to settle. Some pages keep connections open or continuously fetch data; for those, wait for a page-specific landmark instead. For example, replace the navigation and wait logic with:

await page.goto(targetUrl, { waitUntil: 'domcontentloaded', timeout: 45000 });
await page.locator('main h1').waitFor({ state: 'visible', timeout: 15000 });
await page.waitForTimeout(500); // only if the page needs a brief rendering delay

Choose a selector that appears only after the content you need is rendered. A fixed delay is simple, but it can waste time on fast pages and still be too short on slow ones.

3. Choose viewport, locale, and capture scope

Viewport or full page

By default, a screenshot captures the visible viewport. Set fullPage: true to capture the full scrollable page. A long landing page can produce a very tall image; use a viewport capture when reviewing only the hero section or a specific fold.

// Visible viewport
await page.screenshot({ path: 'viewport.png' });

// Entire scrollable page
await page.screenshot({ path: 'full-page.png', fullPage: true });

Desktop and mobile previews

Set viewport dimensions before navigation so responsive CSS is evaluated for the intended display. For a simple mobile layout preview, use a narrow viewport:

const context = await browser.newContext({
  viewport: { width: 390, height: 844 },
  deviceScaleFactor: 1,
  isMobile: true,
  hasTouch: true,
  locale: 'hi-IN'
});

For a named device profile, import devices and spread a supported descriptor into newContext:

import { chromium, devices } from 'playwright';

const browser = await chromium.launch();
const context = await browser.newContext({
  ...devices['iPhone 13'],
  locale: 'hi-IN'
});

Device descriptors emulate browser and display characteristics; they do not reproduce every property of a physical phone. Compare previews captured with the same descriptor and browser version.

Locale is not a font installer

locale: 'hi-IN' configures browser locale behavior, including language-related browser settings. It does not install a Hindi font and does not guarantee that the site’s chosen web font loaded. Inspect the page’s CSS font stack, network-loaded fonts, and installed fonts in the runtime that runs Playwright. Confirm that headings, body copy, numerals, and punctuation render as intended.

If Hindi text appears as boxes, fallback glyphs, or unexpectedly different line breaks, diagnose font loading before changing the viewport. A font can change the width and height of text, which can alter wrapping and the full-page screenshot dimensions.

4. Save JPEG or WebP output

PNG is the default and is suitable for crisp text and visual review. Playwright screenshot options also let you choose JPEG or WebP and adjust image quality where supported. Use a file extension that matches the requested format.

await page.screenshot({ path: 'preview.jpg', type: 'jpeg', quality: 85 });
await page.screenshot({ path: 'preview.webp', type: 'webp', quality: 85 });

Lossy formats can reduce file size, but compression may soften small text and fine borders. Keep PNG for pixel-sensitive comparisons unless storage or transfer size is more important.

5. Make previews repeatable

Screenshot output can vary with the host operating system, browser version, settings, hardware, power source, and headless mode. Playwright recommends using a consistent environment for screenshot comparisons; see its visual comparison guidance.

  • Pin the Playwright version and install its matching browser binaries.
  • Use the same operating system, browser engine, viewport, device scale factor, and locale for each capture.
  • Wait for a meaningful page landmark and any necessary web fonts before capturing.
  • Disable animations for static previews, or otherwise ensure animation state is consistent.
  • Use test data or a stable preview URL when banners, dates, prices, or other content changes.
  • Review screenshot changes before replacing a known-good reference image.

For pages with lazy-loaded images, a full-page screenshot may not match a page that was manually scrolled through first. If below-the-fold assets are missing, scroll through the page in increments before capture or use the site’s own loading signals to determine when those assets are ready.

6. Troubleshooting

Symptom Likely cause Fix
Browser executable is missing The browser binary was not installed for the installed Playwright version, or Playwright was upgraded. Run npx playwright install chromium after installing or upgrading the package. In Linux CI, install required OS dependencies with Playwright’s documented command.
Hindi characters show as boxes or fallback glyphs The needed glyphs are unavailable, a web font failed to load, or the CSS font stack differs in the capture environment. Inspect computed styles and font requests in the actual runtime. Ensure the page’s intended font assets load and verify the host’s installed fonts; locale emulation alone does not supply fonts.
Text wraps differently from the browser preview Viewport width, device scale factor, font availability, or browser version differs. Match those settings and use the same runtime for the reference and new capture.
Navigation times out The site is slow, blocked, or never becomes network idle because of polling or persistent connections. Check that the URL is reachable from the capture host. Use domcontentloaded and wait for a page-specific selector rather than requiring network idle.
Screenshot misses content below the fold The capture is viewport-only, or lazy-loaded content has not been triggered. Set fullPage: true. If lazy content is still absent, scroll through the page before taking the screenshot and wait for the relevant images or sections.
Image dimensions or output look unexpectedly large A full-page capture of a long page creates a tall, high-resolution file; a higher device scale factor increases pixel dimensions. Capture only the viewport when full coverage is unnecessary, reduce the viewport or scale factor, or select JPEG/WebP when lossy compression is acceptable.
Repeated screenshots differ Dynamic content, animation, browser/runtime differences, or changing page data. Stabilize inputs and timing, disable animations, and capture in a fixed environment. Review intentional changes rather than assuming every pixel difference is a defect.

7. Performance, reliability, and cost

Launching a browser has setup overhead, so reuse one browser process for a batch of pages and create a separate context per independent emulation configuration. Close contexts and browsers in a finally block to avoid orphaned processes. Avoid excessive concurrency on memory-limited CI workers, especially for full-page images.

Use the lightest wait condition that still captures the finished content. Waiting for network idle can be unreliable on analytics-heavy or continuously updating pages. A visible selector plus a small, justified render wait is often more predictable. Set timeouts explicitly and record the URL and failure reason so a failed capture can be retried or investigated.

Local Playwright has no per-screenshot service charge, but you pay in development and maintenance time and the compute, browser storage, and CI resources needed to run it. Browser downloads and upgrades also need to be managed. For occasional or repeatable captures without browser installation and automation setup, ScreenshotNeo offers a one-request screenshot API; its usage pricing is listed below.

8. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. It accepts one GET request with a URL and returns an image or PDF. The API uses the parameter names other screenshot APIs use, which can make switching straightforward. See the ScreenshotNeo site and API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/hindi-coaching -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com/hindi-coaching"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com/hindi-coaching'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

In Node.js environments without Bun, save the response body using Node’s filesystem API:

import { writeFile } from 'node:fs/promises';

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com/hindi-coaching'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture; each of those steps can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers say the page verdict and billing status. Its MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Every feature is on every plan. You can also use full-page capture, CSS selectors, device presets, custom CSS or JavaScript, wait conditions, caching, bulk capture, and other documented options.

Create a free account for 1,000 screenshots a month, with no card required.

9. FAQ

Does setting locale: 'hi-IN' translate the page?

No. It sets browser locale behavior. The site must provide Hindi content, and its fonts must render in the capture environment.

Can I use these previews as visual regression baselines?

Yes. Capture the same URL and settings in a consistent environment, then compare new output with the saved baseline and review changes.

Should I use a full-page image for every review?

No. Use full-page output when below-the-fold content matters. A viewport screenshot is easier to inspect when the review is limited to a particular screen size or section.

Will a screenshot include content that requires login?

Only if the browser session is authenticated and the page is authorized to be captured. Configure the context with the session state your application uses, and protect credentials and session data.