When building PDFs dynamically within a React application — for use‑cases such as invoices requiring company logos, reports with embedded charts, or certificates with digital signatures — you often need to insert raster images onto PDF pages programmatically. Native JavaScript lacks built‑in support for modifying PDF internal structures, and spinning up a dedicated backend solely to overlay images is unnecessary overhead for tasks that can run client‑side.
Spire.PDF for JavaScript delivers a complete PDF engine compiled to WebAssembly, allowing your React application to create, modify, and export PDFs fully within the browser. All file operations take place inside the browser‑based Virtual File System (VFS), eliminating network round‑trips. Using the PdfImage class together with the page canvas DrawImage method, you gain precise control over image placement and dimensions inside your PDF files.
In this tutorial, you will learn how to:
- Load an image into the VFS and convert it into a
PdfImageobject - Render images onto PDF pages with custom position and size settings
- Insert images into newly‑created PDFs as well as pre‑existing PDF documents
- Scale, center, and apply images across multiple PDF pages in batches
- Trigger browser‑initiated downloads for your final generated PDF output
Why Generate and Modify PDFs in the Browser
Traditional workflows rely on server‑side PDF libraries including iText, PDFBox and similar tools: the browser uploads source files and image assets, processing happens server‑side, and the generated PDF is sent back to the client. While functional, this approach introduces notable pain points for PDF image‑insertion workflows:
- Latency: Each PDF render depends on network round‑trips; performance degrades noticeably with large documents or poor network conditions.
- Privacy: Original PDFs and image assets must leave the end‑user device, creating data‑exposure risks when handling confidential content.
- Cost: PDF processing is CPU‑intensive, and scaling server‑based PDF operations incurs ongoing infrastructure expenses.
Spire.PDF for JavaScript shifts the entire workload to the browser via WebAssembly. Once the WASM module finishes loading, PDF processing runs locally with instant response times. Documents never leave the user’s device, and no server‑side compute resources are consumed.
Add an image to a new PDF
The simplest scenario: create a brand-new PDF document and draw an image onto its first page. The core steps are:
-
Load the image into the VFS using
window.spire.FetchFileToVFS -
Create a
PdfDocumentand add a blank page -
Create a
PdfImagefrom the loaded file withPdfImage.FromFile -
Draw the image onto the page canvas with
page.Canvas.DrawImage - Save and download the result
function App() {
const addImageToPdf = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check if the WASM module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Load the image into VFS
const inputImageName = 'TreePic.png';
await window.spire.FetchFileToVFS(inputImageName, "", `${process.env.PUBLIC_URL}/data/`);
// Create a PdfDocument object
let doc = new pdfModule.PdfDocument();
// Add a page
let page = doc.Pages.Add();
// Load the image and scale its display size proportionally
let image = pdfModule.PdfImage.FromFile(inputImageName);
let width = image.Width * 0.6;
let height = image.Height * 0.6;
// Calculate the horizontal center position and set the vertical position
let x = (page.Canvas.ClientSize.Width - width) / 2;
let y = 60;
// Draw the image at the specified position on the page
page.Canvas.DrawImage({ image: image, x: x, y: y, width: width, height: height });
// Define the output file name in PDF format
const outputFileName = 'AddImage.pdf';
// Save as PDF format
doc.SaveToFile({ fileName: outputFileName });
doc.Close();
// Read the generated PDF file from VFS and trigger download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Add Image To PDF</h1>
<button onClick={addImageToPdf}>
Generate
</button>
</div>
);
}
export default App;
What the code does:
-
PdfImage.FromFile(inputImageName)reads the image from the VFS and creates aPdfImageobject. The original pixel dimensions are available viaimage.Widthandimage.Height. -
page.Canvas.DrawImage(...)renders the image onto the page. Thexandyparameters set the top-left corner position, andwidthandheightcontrol the display size. - The image is scaled to 60% of its original size (
* 0.6) and horizontally centered using(page.Canvas.ClientSize.Width - width) / 2.
Add an image to an existing PDF
Adding an image to an existing document follows the same pattern — the only difference is that instead of creating a new PdfDocument, you load one from the VFS and select the target page.
const addImageToExistingPdf = async () => {
const pdfModule = window.wasmModule?.spirepdf;
if (!pdfModule) return;
// Load both the PDF and the image into VFS
await window.spire.FetchFileToVFS('Report.pdf', "", `${process.env.PUBLIC_URL}/data/`);
await window.spire.FetchFileToVFS('Logo.png', "", `${process.env.PUBLIC_URL}/data/`);
// Load the existing PDF
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile('Report.pdf');
// Get the first page (or any page you want)
let page = doc.Pages.get_Item(0);
// Load the image and draw it at the top-right corner
let image = pdfModule.PdfImage.FromFile('Logo.png');
let imgWidth = 80;
let imgHeight = 40;
let x = page.Canvas.ClientSize.Width - imgWidth - 30; // 30pt margin from right edge
let y = 30; // 30pt from top
page.Canvas.DrawImage({ image: image, x: x, y: y, width: imgWidth, height: imgHeight });
// Save and download
const outputFileName = 'ReportWithLogo.pdf';
doc.SaveToFile({ fileName: outputFileName });
doc.Close();
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
Key difference from the new-document example: doc.LoadFromFile('Report.pdf') loads an existing PDF instead of starting from scratch, and doc.Pages.get_Item(0) retrieves a page from the loaded document. The rest of the drawing logic is identical.
Scaling and positioning
The DrawImage method gives you full control over where and how large the image appears. Here are the most common patterns:
Proportional scaling — multiply both dimensions by the same factor to preserve the aspect ratio:
let scale = 0.5; // 50% of original size
let width = image.Width * scale;
let height = image.Height * scale;
Fixed width, auto height — set the width and calculate height to preserve the aspect ratio:
let targetWidth = 200;
let width = targetWidth;
let height = image.Height * (targetWidth / image.Width);
Horizontal centering — place the image equidistant from the left and right page margins:
let x = (page.Canvas.ClientSize.Width - width) / 2;
Vertical centering — place the image equidistant from the top and bottom of the page:
let y = (page.Canvas.ClientSize.Height - height) / 2;
Custom position — use absolute coordinates (origin is top-left, units are points; 1 point = 1/72 inch):
let x = 72; // 1 inch from left
let y = 144; // 2 inches from top
Add images to multiple pages
To add the same image (e.g., a logo or watermark) to every page in a document, loop through the Pages collection:
const addImageToAllPages = async () => {
const pdfModule = window.wasmModule?.spirepdf;
if (!pdfModule) return;
await window.spire.FetchFileToVFS('Business_Data_Overview.pdf', "", `${process.env.PUBLIC_URL}/data/`);
await window.spire.FetchFileToVFS('Logo.png', "", `${process.env.PUBLIC_URL}/data/`);
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile('Business_Data_Overview.pdf');
let image = pdfModule.PdfImage.FromFile('Logo.png');
let imgWidth = 60;
let imgHeight = 30;
// Loop through all pages and draw the logo in the top-right corner
for (let i = 0; i < doc.Pages.Count; i++) {
let page = doc.Pages.get_Item(i);
let x = page.Canvas.ClientSize.Width - imgWidth - 20;
let y = 20;
page.Canvas.DrawImage({ image: image, x: x, y: y, width: imgWidth, height: imgHeight });
}
const outputFileName = 'AllPagesWithLogo.pdf';
doc.SaveToFile({ fileName: outputFileName });
doc.Close();
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
This pattern is useful for adding watermarks, company logos, or page stamps uniformly across a multi-page document.
Download the result
After saving the PDF to the VFS with doc.SaveToFile(), you need to read it back and trigger a browser download. This two-step pattern — save to VFS, then read from VFS — is used in every Spire.PDF for JavaScript example:
// 1. Save the PDF to the VFS
doc.SaveToFile({ fileName: 'Output.pdf' });
doc.Close();
// 2. Read the file from VFS as a byte array
const fileArray = window.dotnetRuntime.Module.FS.readFile('Output.pdf');
// 3. Create a Blob and trigger download
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'Output.pdf';
a.click();
URL.revokeObjectURL(url);
With Spire.PDF for JavaScript, all PDF image editing and generation tasks run locally in the browser via WebAssembly. After initializing the WASM module, all rendering operations execute instantly on the client side. No files are uploaded externally, no server resources are required, and the entire PDF image embedding process is faster, safer, and cost-free.

Top comments (0)