How to Convert HTML Only to JPG
Render your HTML in a browser, capture the viewport or full page, and save a real JPG with Playwright, Puppeteer, Chrome, wkhtmltoimage, or ScreenshotNeo.
Direct answer: HTML is markup, so you cannot convert it to JPG by renaming the file. Load the document in a browser or HTML-to-image renderer, wait for its styles and scripts to finish, capture the viewport or the full scrollable page, and request JPEG output. For a local file, browser automation is usually the most reliable route.
Use Playwright when you need explicit JPEG and full-page controls. Puppeteer follows the same browser-rendering model. Chrome Headless is useful for command-line captures but its documented example writes PNG, so add a separate conversion step when JPG is required. wkhtmltoimage can render a local HTML file directly and exposes settings for JavaScript, images, delay, screen width, quality and local-file access.
1. Decide what “HTML to JPG” means
| Requirement | Recommended approach | Important setting |
|---|---|---|
| Visible browser window | Playwright or Puppeteer | Viewport width and height |
| Entire long document | Playwright full-page capture | Full-page mode and page layout |
| One-off command | wkhtmltoimage or Chrome Headless | Input path, output path and wait behavior |
| Dynamic JavaScript content | Playwright, Puppeteer or configured wkhtmltoimage | Wait for a selector, delay or network completion |
| Nearby images, CSS or fonts | Local browser rendering | Correct relative paths and local-file permissions |
Also choose your image dimensions before capturing. A narrow viewport can trigger mobile CSS, while a wide viewport can change line wrapping and page height. If the image is for a report or social preview, set the viewport explicitly instead of relying on a desktop default.
2. Convert a local HTML file with Playwright
Playwright’s screenshot interface supports JPEG, a custom output filename and full-page capture. The following example opens a local file, waits for the page to settle, and writes a JPG.
Install
npm install playwright
npx playwright install chromium
Node.js: viewport JPG
const { chromium } = require('playwright');
const path = require('node:path');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage({
viewport: { width: 1440, height: 900 },
deviceScaleFactor: 1
});
const fileUrl = 'file://' + path.resolve('page.html');
await page.goto(fileUrl, { waitUntil: 'networkidle' });
await page.screenshot({
path: 'page.jpg',
type: 'jpeg',
quality: 90,
fullPage: false
});
await browser.close();
})();
Node.js: full-page JPG
const { chromium } = require('playwright');
const path = require('node:path');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1280, height: 900 } });
await page.goto('file://' + path.resolve('page.html'), { waitUntil: 'networkidle' });
await page.screenshot({
path: 'page-full.jpg',
type: 'jpeg',
quality: 88,
fullPage: true
});
await browser.close();
})();
Use fullPage: false for the visible viewport. Use fullPage: true when you need the complete scrollable document. Very long pages can create extremely tall JPEGs; split them into sections if the destination has image-size limits.
Python: Playwright
from pathlib import Path
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page(viewport={"width": 1440, "height": 900})
page.goto(Path("page.html").resolve().as_uri(), wait_until="networkidle")
page.screenshot(
path="page.jpg",
type="jpeg",
quality=90,
full_page=True,
)
browser.close()
Install the Python package and browser binaries with:
pip install playwright
playwright install chromium
Wait for content that appears after load
await page.goto(fileUrl, { waitUntil: 'domcontentloaded' });
await page.waitForSelector('#report-ready', { state: 'visible', timeout: 30000 });
await page.screenshot({ path: 'ready.jpg', type: 'jpeg', quality: 90, fullPage: true });
A selector wait is safer than an arbitrary sleep when your page has a known completion marker. If no marker exists, use a short delay after navigation or wait for network idle. Pages that continuously poll or stream data may never become network-idle; in that case, wait for a specific element or use a bounded timeout.
3. Render HTML with Puppeteer
Puppeteer’s Page API uses the same basic sequence: launch a browser, navigate to a URL, call page.screenshot(), then close the browser. A local HTML document can be opened through a file:// URL.
npm install puppeteer
const puppeteer = require('puppeteer');
const path = require('node:path');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });
await page.goto('file://' + path.resolve('page.html'), {
waitUntil: 'networkidle0',
timeout: 30000
});
await page.screenshot({
path: 'page.jpg',
type: 'jpeg',
quality: 90,
fullPage: true
});
await browser.close();
})();
Check the Puppeteer version installed in your project for the exact screenshot options it accepts. Keep the browser and package versions pinned in CI so layout changes are intentional.
4. Use Chrome Headless from the command line
Chrome documents --headless, --screenshot, --window-size and a timeout flag. Its documented example saves screenshot.png, so treat Chrome’s direct output as PNG unless the Chrome version you use explicitly supports another format.
google-chrome \
--headless \
--disable-gpu \
--window-size=1440,900 \
--screenshot=page.png \
file:///absolute/path/page.html
Convert the PNG to JPG with an image tool that your environment already uses, for example ImageMagick:
magick page.png -quality 90 page.jpg
When the page needs more time to execute JavaScript, use Chrome’s documented timeout control or switch to Playwright/Puppeteer, where selector and network waits are easier to express.
5. Use wkhtmltoimage for a direct HTML-to-image command
The wkhtmltoimage command accepts an input HTML file and an output image path:
wkhtmltoimage [OPTIONS]... <input file> <output file>
For example:
wkhtmltoimage \
--width 1440 \
--quality 90 \
--javascript-delay 1500 \
page.html page.jpg
Its options include JavaScript enablement, JavaScript delay, image loading, screen width and local-file access. If your HTML references local CSS, images or fonts, check the installed package’s help or manpage and enable the appropriate local-file behavior. The Ubuntu Noble manpage describes version 0.12.6-2build2; distributions can ship different builds and defaults.
6. Make local assets render correctly
- Use relative paths that resolve from the HTML file. For example,
src="assets/logo.png"expects anassetsdirectory beside the document. - Prefer absolute file URLs while debugging. Print the resolved path and open
file:///...directly. - Check browser console errors. A missing stylesheet can make the JPG look unstyled even though the screenshot command succeeds.
- Handle external assets deliberately. Network images, web fonts and scripts can fail in offline CI. Bundle them locally or wait for them explicitly.
- Allow local-file access only when required. Some HTML-to-image tools restrict local resources for security reasons; use the tool’s documented setting rather than changing unrelated browser flags.
7. Control layout, quality and color
- Viewport: Set width and height to match the intended output. Responsive breakpoints can produce a different design at 768px than at 1440px.
- Device scale factor: A higher scale factor captures more pixels but increases memory and file size.
- JPEG quality: Values around 85–95 are a practical starting point. Lower quality creates smaller files with more visible artifacts around text and sharp edges.
- Transparency: JPEG has no alpha channel. Give the page an explicit background color before capture, or use PNG/WebP when transparency matters.
- Fonts: Wait until web fonts have loaded if typography must match the browser view. A fallback font changes line wrapping and full-page height.
- Animations: Disable or freeze animations when deterministic output matters. Otherwise two captures can differ depending on timing.
8. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| JPG is blank or mostly white | Capture happened before rendering completed | Wait for a ready selector, network idle, or a bounded delay; verify the page URL. |
| Styles or images are missing | Relative paths or local-file permissions are wrong | Resolve paths from the HTML directory and check console/network errors. |
| Only the top portion appears | Viewport capture was requested | Enable full-page capture or increase the output height. |
| Page is unexpectedly mobile | Viewport width crossed a responsive breakpoint | Set an explicit desktop width before navigation. |
| JavaScript data is absent | Screenshot was taken before the app finished | Wait for the element containing the data, not just document load. |
| Fonts change between runs | Web fonts are still loading or unavailable | Bundle fonts, wait for font readiness, or run with a stable network. |
| Chrome produces PNG instead of JPG | The documented Chrome flag writes PNG | Convert the PNG afterward or use Playwright/Puppeteer with JPEG output. |
| wkhtmltoimage cannot read local assets | Local-file access is restricted | Review the installed tool’s local-file option and pass the correct permission. |
| Automation times out | Page keeps making requests or a resource is unavailable | Use a selector wait with a timeout, block optional resources, or provide local fallbacks. |
| Very tall JPG fails to save | Image dimensions exceed memory or downstream limits | Capture sections, reduce scale, or produce multiple images. |
9. Performance and reliability
- Reuse one browser process for multiple files, creating a new page or context per document.
- Use a fixed viewport, browser version and font set in CI to reduce layout drift.
- Set navigation and selector timeouts so a broken page cannot stall a batch indefinitely.
- Block analytics, ads and other nonessential requests when they are not part of the visual result.
- Capture after the page reaches a meaningful application state. “Network idle” is not sufficient for applications that poll continuously.
- For long pages, estimate memory from width × height × device scale factor before enabling full-page capture.
- Keep the source HTML, browser version and capture settings with generated assets so a changed JPG can be reproduced.
10. Or skip the browser setup
ScreenshotNeo renders a public URL and returns a PNG, JPG, WebP or PDF through one request. 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 reports the page verdict and billing status in headers. Its MCP server gives Claude, Cursor and other MCP clients screenshot, page-info and PDF tools.
For an HTML file, publish it at a reachable URL first, then call the API. See the ScreenshotNeo API documentation for all options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.jpg
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com"}, timeout=90)
open("shot.jpg", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
const data = Buffer.from(await res.arrayBuffer());
require('node:fs').writeFileSync('shot.jpg', data);
You get 1,000 screenshots per month free with no card. Paid plans start at $5 for 3,000 shots, and every feature is available on every plan. Create a free ScreenshotNeo account.
11. FAQ
Can I convert HTML to JPG without opening a visible browser?
Yes. Playwright, Puppeteer, Chrome Headless and wkhtmltoimage run headlessly. They still render the document before producing the image.
Should I use JPG or PNG?
Use JPG for smaller photographic or web-preview files. Use PNG when you need transparency, crisp text, or lossless output.
Why does my full-page image look different from the browser window?
Full-page capture lays out the document at the chosen viewport width and then extends the capture height. Responsive rules, lazy loading and sticky elements can change what appears during the extended capture.
Can a local HTML file load remote JavaScript?
It depends on browser security rules, network availability and the remote server’s policies. For repeatable builds, bundle critical assets locally or serve the document from a local HTTP server.
How do I convert many HTML files?
Launch one browser, process files in pages or contexts with bounded timeouts, and write unique output names. Limit concurrency so memory use stays predictable.


