All free tools

HTML viewer

Paste or open HTML and see it rendered as you type.

Preview

If your HTML is going to be a PDF, don't judge it in Desktop view. Switch to A4 page and turn on Print CSS: you'll see it at the width a PDF page gives it, with the styles its print rules set. That's the check worth doing before you convert anything.

Why does my HTML look different as a PDF?

Two things change when HTML becomes a PDF page: the width and the styles.

The width comes from the paper size. An A4 page is 210 mm wide, and at 96 CSS pixels to the inch that's about 794 pixels. A layout that's fine on a wide screen can wrap, squeeze or flip to its phone layout at that width. A4 page shows your HTML at 794 pixels, so you see it before your customer does.

The styles come from print rules. A stylesheet's @media print rules say how the document should look on paper: hide the navigation, drop the buttons, change the colors. Print CSS applies them and switches off the screen-only rules. In PDFShift, that's the use_print option, and it's off by default. So if your HTML relies on print rules, send use_print: true. If it doesn't, leave Print CSS off here too.

The invoice already in the editor has print rules. Switch to A4 page, then tick Print CSS: the navigation bar and the Pay online button go, and the invoice is all that's left. That's the version you want in the PDF.

Print CSS hides what a printed page doesn't needTwo sketches of the same invoice at A4 width. On the left, Print CSS off: a dark navigation bar, the invoice title, a table and a purple Pay online button. On the right, Print CSS on: the navigation bar and the button are gone, and the invoice title and table remain.Print CSS offPrint CSS on (use_print: true)Dashboard Invoices SettingsPay online
The sample invoice at A4 page width, with Print CSS off and on.

The preview is one long page. It doesn't show page breaks, margins, headers or footers: those only appear in the PDF itself. Print CSS reads the print rules in <style> tags and in the media attribute of <link> tags. It doesn't pick up an @media print block inside a linked stylesheet file.

Why are images or fonts missing in the preview?

Because the preview can't find your files. Relative URLs in pasted HTML are read against the address of this page, so <img src="logo.png"> asks pdfshift.io for a file it doesn't have. Stylesheets and fonts loaded by relative path break the same way.

The fix is one line. Add a <base> tag at the top of <head> with the address your files live at, or write the URLs out in full.

A base tag sends relative paths back to your own siteWithout a base tag, img src logo.png resolves to https://pdfshift.io/tools/html-viewer/logo.png, which doesn't exist. With base href https://example.com/invoices/, it resolves to https://example.com/invoices/logo.png, where the file is.Without a base tagsrc="logo.png"pdfshift.io/tools/html-viewer/logo.pngNot found: the image doesn't showWith <base href="https://example.com/invoices/">src="logo.png"example.com/invoices/logo.png
Where a relative image path points, with and without a base tag.
<head>
  <base href="https://example.com/invoices/">
  ...
</head>

Is my HTML sent anywhere?

No. Your browser draws it in an <iframe> on this page, and nothing in the tool uploads it. Files you open are read by your browser too, up to 2 MB.

The frame is sandboxed. Scripts stay off until you tick Run JavaScript. Forms show but don't submit, links that open new windows do nothing, and even with scripts on, the HTML can't read pdfshift.io's cookies or move you to another page.

Turn the HTML into a PDF

When it looks right, send the same HTML to PDFShift as the source of one request. We render it in Chromium and send the PDF back, so there's no browser for you to run or patch. Here it is in Node 18 or later:

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

const html = await readFile('invoice.html', 'utf8');

const response = await fetch('https://api.pdfshift.io/v3/convert/pdf', {
    method: 'POST',
    headers: {
        'X-API-Key': process.env.PDFSHIFT_API_KEY,
        'Content-Type': 'application/json'
    },
    // use_print: true applies your @media print rules, like Print CSS above
    body: JSON.stringify({ source: html, use_print: true })
});

if (!response.ok) {
    throw new Error(`PDFShift returned ${response.status}: ${await response.text()}`);
}

await writeFile('invoice.pdf', Buffer.from(await response.arrayBuffer()));

The free plan gives you 50 credits a month with no credit card: create an account to get an API key. For the same call in other languages, see converting raw HTML to PDF in our guides.