Use NestJS to produce the HTML, then let a headless browser such as Puppeteer render that HTML and capture pixels. NestJS view engines are responsible for generating markup; they do not convert markup into PNG or JPEG by themselves. A production implementation normally separates those jobs into a rendering service, a browser capture service, and an HTTP controller that returns the resulting bytes.
This guide shows a complete Puppeteer implementation, readiness and sizing decisions, error handling, alternatives, and an API option when you do not want to package Chromium with your NestJS application.
How the conversion pipeline works
The flow has four distinct stages:
- Build HTML, either from a string or a NestJS view template.
- Open a page in a managed Chromium instance.
- Load the HTML, wait for fonts, images, and client-side rendering to finish.
- Call
page.screenshot()and return the buffer with an image content type.
NestJS documents view-engine setup and the @Render() decorator for passing handler data into a template (NestJS MVC documentation). Puppeteer documents page and element screenshots, output formats, clipping, paths, and full-page capture (Puppeteer screenshots guide and ScreenshotOptions API).
Install NestJS and Puppeteer dependencies
For a service that launches Puppeteer directly, install:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
#1 Best Overall
- CRISP CLARITY: This 23.8″ Philips V line monitor delivers crisp Full HD 1920x1080 visuals. Enjoy movies, shows and videos with remarkable detail
- INCREDIBLE CONTRAST: The VA panel produces brighter whites and deeper blacks. You get true-to-life images and more gradients with 16.7 million colors
- THE PERFECT VIEW: The 178/178 degree extra wide viewing angle prevents the shifting of colors when viewed from an offset angle, so you always get consistent colors
- WORK SEAMLESSLY: This sleek monitor is virtually bezel-free on three sides, so the screen looks even bigger for the viewer. This minimalistic design also allows for seamless multi-monitor setups that enhance your workflow and boost productivity
- A BETTER READING EXPERIENCE: For busy office workers, EasyRead mode provides a more paper-like experience for when viewing lengthy documents
npm install puppeteer
puppeteer downloads a compatible browser during installation. In a container or a restricted CI environment, confirm that the browser executable and required Linux libraries are available. If your organization already manages Chromium, use puppeteer-core and provide an explicit executable path instead.
A Nest-specific package, nestjs-puppeteer, provides module configuration and dependency injection for a Puppeteer Browser. Its npm listing currently reports version 3.1.0 and peer compatibility with NestJS 10/11 and Puppeteer 22–24; verify the package metadata before installing because those ranges can change (nestjs-puppeteer on npm).
Create a reusable screenshot service
The service below receives already-rendered HTML, reuses one browser process, creates a fresh page per request, and always closes that page. A fresh page prevents cookies, local storage, and DOM state from leaking between jobs.
import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
import puppeteer, { Browser, ScreenshotOptions } from 'puppeteer';
@Injectable()
export class HtmlScreenshotService implements OnModuleInit, OnModuleDestroy {
private browser!: Browser;
async onModuleInit() {
this.browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
}
async onModuleDestroy() {
await this.browser?.close();
}
async capture(html: string, options: ScreenshotOptions = {}) {
const page = await this.browser.newPage();
try {
await page.setViewport({
width: 1200,
height: 800,
deviceScaleFactor: 1,
});
await page.setContent(html, { waitUntil: 'networkidle0' });
await page.evaluate(() => document.fonts.ready);
return await page.screenshot({
type: 'png',
fullPage: true,
...options,
});
} finally {
await page.close();
}
}
}
networkidle0 waits for a period with no active network requests, but it is not a universal application-ready signal. Analytics, WebSockets, polling, or deferred rendering can keep a page busy indefinitely—or finish before your framework has painted the required content. Use an application-specific selector or readiness promise when that is more reliable.
Render a NestJS template before capturing it
When the source is a template rather than a literal string, configure a view engine and render the template to HTML. For example, with Handlebars:
npm install hbs
import { NestFactory } from '@nestjs/core';
import { join } from 'node:path';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.setBaseViewsDir(join(__dirname, '..', 'views'));
app.setViewEngine('hbs');
await app.listen(3000);
}
bootstrap();
A controller can render a view for a normal browser response with @Render('invoice'). For image generation, the simplest reliable pattern is to render the template in the service (for example with the view engine’s compiler), then pass the resulting string to HtmlScreenshotService.capture(). Keeping template rendering separate from browser capture makes it easier to test both parts and to reuse the capture service for HTML assembled from data.
Rank #2
- CRISP CLARITY: This 22 inch class (21.5″ viewable) Philips V line monitor delivers crisp Full HD 1920x1080 visuals. Enjoy movies, shows and videos with remarkable detail
- 100HZ FAST REFRESH RATE: 100Hz brings your favorite movies and video games to life. Stream, binge, and play effortlessly
- SMOOTH ACTION WITH ADAPTIVE-SYNC: Adaptive-Sync technology ensures fluid action sequences and rapid response time. Every frame will be rendered smoothly with crystal clarity and without stutter
- INCREDIBLE CONTRAST: The VA panel produces brighter whites and deeper blacks. You get true-to-life images and more gradients with 16.7 million colors
- THE PERFECT VIEW: The 178/178 degree extra wide viewing angle prevents the shifting of colors when viewed from an offset angle, so you always get consistent colors
Return a PNG from a NestJS endpoint
The controller below accepts a validated data object, creates HTML, captures it, and sends the bytes. In a real application, use a template engine rather than interpolating untrusted strings directly.
import { Controller, Get, Res } from '@nestjs/common';
import { Response } from 'express';
import { HtmlScreenshotService } from './html-screenshot.service';
@Controller('cards')
export class CardsController {
constructor(private readonly screenshots: HtmlScreenshotService) {}
@Get('preview.png')
async preview(@Res() res: Response) {
const html = `<!doctype html>
<html><head><style>
body { margin: 0; font-family: Arial, sans-serif; }
.card { width: 900px; padding: 48px; background: #fff; }
</style></head>
<body><div class="card">NestJS preview</div></body></html>`;
const image = await this.screenshots.capture(html, {
type: 'png',
fullPage: true,
});
res.set({
'Content-Type': 'image/png',
'Content-Length': image.length.toString(),
'Cache-Control': 'no-store',
});
res.send(image);
}
}
For JPEG or WebP, set type: 'jpeg' or type: 'webp'. JPEG accepts a quality value; PNG preserves transparency and does not use JPEG quality. If you need a file instead of an HTTP response, pass path: '/absolute/output/card.png' and return the path or upload the file to object storage.
Choose the screenshot scope and dimensions
Viewport or full page
A normal screenshot captures the current viewport. fullPage: true expands the capture to the document’s full scrollable height, which is useful for reports and invoices. Very tall pages can consume substantial memory; consider splitting a long document or generating a PDF when pagination matters.
Capture one element
To capture a component rather than the whole document, locate it and call the element screenshot API:
const element = await page.waitForSelector('.receipt');
if (!element) throw new Error('Receipt element was not rendered');
const image = await element.screenshot({ type: 'png' });
This avoids calculating a clip rectangle yourself and naturally follows the element’s rendered bounds.
Viewport, scale, and responsive layout
page.setViewport() controls CSS pixels. deviceScaleFactor controls output density: a factor of 2 produces retina-like pixels while preserving the same CSS layout. Set the viewport to the layout you actually want to test—desktop, mobile, or a custom social-card size—and verify the resulting pixel dimensions with your installed Puppeteer version.
Recommended Free Tools
Rank #3
- Clear visuals. Fluid motion: A 144Hz refresh rate and 1ms MPRT deliver smooth, tear‑free motion across work, gaming, and streaming for clearer, more fluid viewing.
- Eye comfort: TÜV Rheinland 3‑star* certification reduces harmful blue light while preserving stunning color quality without compromise. *TÜV Rheinland 3-star eye comfort certification.
- Wide viewing angle: Get consistent views across a wide 178° /178° viewing angle.
- In-Plane Switching (IPS): See excellent color accuracy and consistency across wide viewing angles with In-plane Switching (IPS) technology.
- Ultra-thin bezels: Maximize your viewing experience with thin bezels.
Backgrounds, clipping, and transparency
Use screenshot options for clipping to a rectangle, hiding the default background where supported, and choosing image type. Transparent output requires page CSS and screenshot settings that preserve alpha; test with your chosen browser version because page backgrounds and format support interact.
Make readiness deterministic
Waiting is the most common source of incomplete images. Prefer a known application condition over an arbitrary delay:
await page.setContent(html, { waitUntil: 'domcontentloaded' });
await page.waitForSelector('#chart[data-ready="true"]', { timeout: 15000 });
await page.evaluate(() => document.fonts.ready);
await page.screenshot({ type: 'png', fullPage: true });
- Use
waitForSelectorafter client-side rendering has inserted the required element. - Use
document.fonts.readyso text is not captured with fallback fonts. - Ensure images have loaded; add an application flag after your image promises resolve.
- Use a bounded timeout and report which condition failed.
There is no universal wait duration. A network-idle event can be inappropriate for pages with persistent requests, while a fixed delay can be either wasteful or too short.
Secure and productionize the service
Validate inputs
Do not expose an endpoint that accepts arbitrary HTML or remote URLs without controls. Sanitize or template data, restrict allowed assets and protocols, and enforce maximum HTML size. Otherwise an attacker may inject scripts, read internal network resources, or exhaust memory.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Control concurrency
Chromium is expensive. Reuse a browser process, limit simultaneous pages with a queue or semaphore, and reject work when the queue is full. Set navigation and selector timeouts, and close pages in finally blocks even when capture fails.
Plan deployment
Package the browser binary and its system dependencies in your image, or configure an explicit executable path for a managed browser. Test cold startup, font availability, image loading, and the maximum document height in the same container image used in production. The sources do not establish a universal memory limit or performance winner, so measure your own templates and concurrency.
Rank #4
- CURVED FOR ENHANCED ENGAGEMENT: An immersive viewing experience with a curved monitor that wraps more closely around your field of vision; It creates a wider view, enhancing depth perception and minimizing peripheral distraction
- SMOOTH PERFORMANCE FOR SEAMLESS CONTENT: Stay in the action when playing games, watching videos, or working on creative projects; The 100Hz refresh rate reduces lag and motion blur so you don't miss a thing in fast-paced moments¹
- MORE GAMING POWER: Gain the edge with optimizable game settings; Color and image contrast can be adjusted to see scenes more vividly and spot enemies hiding in the dark; Game Mode adjusts any game to fill the screen so you can view every detail²
- KEEP IT EASY ON THE EYES: Care for your eyes and stay comfortable, even during long sessions; Advanced eye comfort technology certified by TÜV reduces eye strain by minimizing blue light and reducing irritating screen flicker²
- INCREASED VERSATILITY: Connect to more; Plug devices straight into your monitor for increased flexibility, making your computing environment even more convenient
Puppeteer or Playwright?
Playwright also exposes page.screenshot() in its Page API (Playwright Page API). Choose based on the browser engines you need, existing project dependencies, team familiarity, deployment packaging, and the compatibility of any Nest integration package. Both provide screenshot APIs; available evidence does not establish a universal speed or reliability winner.
Troubleshooting common failures
Chromium fails to launch
Cause: missing shared libraries, sandbox restrictions, or an unavailable executable. Fix: install the dependencies required by your base image, use the browser path supplied by your platform, and only use --no-sandbox in an environment where you understand the isolation trade-off.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →The image is blank or cut off
Cause: capture occurred before client rendering, fonts, or images finished. Fix: wait for a meaningful selector, await fonts, and expose an explicit “ready” marker from the page.
Network-idle never completes
Cause: polling, analytics, WebSockets, or long-lived requests. Fix: use domcontentloaded followed by application-specific readiness, and abort or ignore nonessential requests when appropriate.
Styles or images are missing
Cause: relative URLs have no usable base, blocked resources, or authentication is absent. Fix: include a <base href="..."> element or absolute asset URLs, make resources reachable from the browser, and configure headers or cookies for protected assets.
Requests time out under load
Cause: too many concurrent pages or a browser leak. Fix: cap concurrency, reuse the browser, close every page, and record durations and failure reasons so you can tune limits from real workload data.
Best Value
- 【INTEGRATED SPEAKERS】Whether you're at work or in the midst of an intense gaming session, our built-in speakers provide rich and seamless audio, all while keeping your desk clutter-free.
- 【EASY ON THE EYES】 Protect your eyes and enhance your comfort with Blue-Light Shift technology. This feature reduces harmful blue light emissions from your screen, helping to alleviate eye strain during long hours of use and promoting healthier viewing habits.
- 【WIDEN YOUR PERSPECTIVE】Our sleek minimal bezel design ensures undivided attention. The nearly bezel-free display seamlessly connects in a dual monitor arrangement, delivering an unobstructed view that lets you focus on more at once, completely distraction-free.
Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result.
One GET request returns PNG, JPEG, WebP, or PDF. The API also supports full-page and element capture, device presets and custom viewports, retina scale, custom CSS and JavaScript, selector or network-idle waits, headers and cookies, blocking rules, caching, signed links, asynchronous webhooks, bulk capture, and an MCP server with take_screenshot, get_page_info, and capture_pdf tools for AI clients.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for options and response headers. The Free plan includes 1,000 shots each month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to get started.
Equivalent calls in Python and Node.js
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
Frequently Asked Questions
Can NestJS convert HTML to an image without Puppeteer?
NestJS itself renders responses and templates; a browser engine such as Puppeteer or Playwright is still needed to turn rendered HTML and CSS into pixels unless you use an external screenshot API.
PC 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 & 11Outdated 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 matchShould I return a Buffer or save a file?
Return a Buffer for an HTTP endpoint or in-memory upload. Use Puppeteer’s path option when a local artifact is required, then move it to durable storage.
Can I capture an authenticated page?
Yes, configure the browser page with the required cookies or headers before loading the HTML, and ensure those credentials cannot be supplied by untrusted callers.
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.

