The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →To create a PDF in the browser, use a PDF-authoring library such as pdf-lib: create a document, add a page, draw content, then save the bytes and offer them as a download. That is different from displaying an existing PDF, which is what Mozilla’s PDF.js is designed to do.
Create and download a PDF with pdf-lib
This example uses a JavaScript module environment, such as an app built with a bundler. Install pdf-lib with npm install pdf-lib, then create a document and turn its saved bytes into a browser download.
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';
async function downloadPdf() {
const pdfDoc = await PDFDocument.create();
const page = pdfDoc.addPage([595.28, 841.89]); // A4-sized page in PDF points
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
page.drawText('Hello from the browser', {
x: 50,
y: 780,
size: 18,
font,
color: rgb(0, 0, 0),
});
const pdfBytes = await pdfDoc.save();
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
const objectUrl = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = objectUrl;
link.download = 'example.pdf';
document.body.append(link);
link.click();
link.remove();
URL.revokeObjectURL(objectUrl);
}
document.querySelector('#download-pdf').addEventListener('click', downloadPdf);
Place a button with id="download-pdf" in the page to trigger the example. The download happens in response to a user click, which is a dependable pattern for browser downloads. The page dimensions above are expressed in PDF points; you can pass different width and height values to addPage when you need a different layout.
For a simple preview rather than a download, pdf-lib’s quick start demonstrates serializing to a data URI and assigning it to an iframe’s src:
Recommended Free Tools
#1 Best Overall
const dataUri = await pdfDoc.saveAsBase64({ dataUri: true });
document.querySelector('#pdf-preview').src = dataUri;
Add an iframe with id="pdf-preview" for this version. A preview is useful when someone should inspect the generated document before deciding whether to save it.
Choose a library for the PDF task
| Need | Suitable direction | What it does |
|---|---|---|
| Create a new PDF or modify one programmatically | pdf-lib | Creates and modifies PDFs, including drawing text, embedding images and fonts, filling forms, and copying or combining pages. |
| Render and inspect an existing PDF | PDF.js | Provides a display layer for rendering PDFs and retrieving document information; it is complementary to an authoring library, not the creation path shown here. |
pdf-lib uses explicit page coordinates for drawing. Do not assume that applying ordinary webpage CSS to a document will reproduce the same layout in a PDF; the workflow here composes PDF content directly. If you need to import a remote PDF or image, fetch its bytes before loading or embedding it. Verify that the asset can be requested from your app’s origin and handle network failures before proceeding.
Rank #2
Load an existing PDF or remote asset
pdf-lib can load an existing PDF for changes, and its examples fetch remote documents and assets as bytes before loading or embedding them. A minimal pattern for a PDF is:
const response = await fetch('/documents/source.pdf');
if (!response.ok) {
throw new Error(`Could not fetch PDF: ${response.status}`);
}
const existingPdfBytes = await response.arrayBuffer();
const pdfDoc = await PDFDocument.load(existingPdfBytes);
// Make changes to pdfDoc here.
const updatedPdfBytes = await pdfDoc.save();
The URL in this example is a same-origin example path; replace it with the actual resource URL. For remote origins, the server must permit the browser request under its cross-origin policy. Fetching the bytes successfully is a separate prerequisite from whether pdf-lib can parse or embed them.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Browser implementation details that affect reliability
- Wait for asynchronous work. Creating, loading, embedding and saving documents are asynchronous steps. Await them before creating the download or preview.
- Revoke download URLs after use.
URL.createObjectURLallocates a temporary URL. The example revokes it after triggering the download; for a UI that may open it later, revoke it when it is no longer needed. - Use an explicit output type. Set the Blob MIME type to
application/pdfso browser handling identifies the file as a PDF. - Check remote requests. A failed fetch, blocked cross-origin request, or invalid PDF input must be handled before loading or embedding bytes.
- Test the actual target browsers and document sizes. The available project documentation does not establish a complete browser compatibility matrix or quantitative performance limits, so do not assume a particular size or speed ceiling without testing your app.
- Pin CDN versions in production. If loading a UMD build from a CDN, pdf-lib recommends specifying a version in the URL. Check the current release and its documentation before choosing the pinned version.
Or skip the browser setup
If what you need is a capture of a webpage rather than a custom document assembled from application data, ScreenshotNeo can return a website screenshot or PDF from an API request. It is not a replacement for composing arbitrary PDF pages with pdf-lib. Its API also accepts or removes cookie banners, newsletter popups and chat widgets before capture; bot checks, blank pages and failed loads are not billed; and its MCP server lets AI agents take screenshots.
Here is the supplied one-call cURL example for a webpage capture. See the ScreenshotNeo documentation for PDF output options and other request parameters.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Sign up for free.
Quick Recap
Best Value
Rank #4
Troubleshooting
- The button does nothing: Check the button’s selector and ensure the event listener is registered after the relevant element exists. Look for rejected promises in the browser console.
- The generated file is empty or incomplete: Confirm all content-building steps finish before
save()runs, and check that any fetched source or image bytes were loaded successfully. - A remote PDF or image will not load: Inspect the network response and browser console. Confirm the URL is correct and the remote server permits the browser request; handle non-success HTTP responses before passing bytes to pdf-lib.
- The preview does not appear: Ensure the iframe exists and the data URI is assigned after serialization completes. For larger outputs, consider an object URL backed by a Blob instead of embedding a data URI.
- Text or images are positioned incorrectly: Recheck the PDF page dimensions and the coordinates passed to drawing methods. The authoring model is coordinate-based, not a direct transfer of HTML layout.
References
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.
Free tools Windows power users keep installed
One-click scans. No signup required.




